diff options
Diffstat (limited to 'src/qml/qml')
121 files changed, 14822 insertions, 8120 deletions
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index ade05a596b..eadba394b4 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -3,6 +3,7 @@ HEADERS += \ $$PWD/qintrusivelist_p.h \ $$PWD/qpodvector_p.h \ $$PWD/qhashedstring_p.h \ + $$PWD/qprimefornumbits_p.h \ $$PWD/qqmlrefcount_p.h \ $$PWD/qfieldlist_p.h \ $$PWD/qqmlthread_p.h \ @@ -11,12 +12,14 @@ HEADERS += \ $$PWD/qrecyclepool_p.h \ $$PWD/qflagpointer_p.h \ $$PWD/qlazilyallocated_p.h \ - $$PWD/qqmlnullablevalue_p.h + $$PWD/qqmlnullablevalue_p.h \ + $$PWD/qstringhash_p.h \ + $$PWD/qlinkedstringhash_p.h SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qqmlthread.cpp \ + $$PWD/qqmlthread.cpp # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri # clock_gettime() is implemented in librt on these systems diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h index 71b41cd30b..a10e57aeca 100644 --- a/src/qml/qml/ftw/qflagpointer_p.h +++ b/src/qml/qml/ftw/qflagpointer_p.h @@ -55,6 +55,17 @@ QT_BEGIN_NAMESPACE +namespace QtPrivate { +template <typename T> struct QFlagPointerAlignment +{ + enum : size_t { Value = Q_ALIGNOF(T) }; +}; +template <> struct QFlagPointerAlignment<void> +{ + enum : size_t { Value = ~size_t(0) }; +}; +} + template<typename T> class QFlagPointer { public: @@ -133,6 +144,7 @@ template<typename T> QFlagPointer<T>::QFlagPointer(T *v) : ptr_value(quintptr(v)) { + Q_STATIC_ASSERT_X(Q_ALIGNOF(T) >= 4, "Type T does not have sufficient alignment"); Q_ASSERT((ptr_value & FlagsMask) == 0); } @@ -247,6 +259,8 @@ template<typename T, typename T2> QBiPointer<T, T2>::QBiPointer(T *v) : ptr_value(quintptr(v)) { + Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T>::Value >= 4, + "Type T does not have sufficient alignment"); Q_ASSERT((quintptr(v) & FlagsMask) == 0); } @@ -254,6 +268,8 @@ template<typename T, typename T2> QBiPointer<T, T2>::QBiPointer(T2 *v) : ptr_value(quintptr(v) | Flag2Bit) { + Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T2>::Value >= 4, + "Type T2 does not have sufficient alignment"); Q_ASSERT((quintptr(v) & FlagsMask) == 0); } diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index 117670dbfc..7a8fdd0a14 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -39,82 +39,7 @@ #include "qhashedstring_p.h" - - -/* - A QHash has initially around pow(2, MinNumBits) buckets. For - example, if MinNumBits is 4, it has 17 buckets. -*/ -const int MinNumBits = 4; - -/* - The prime_deltas array is a table of selected prime values, even - though it doesn't look like one. The primes we are using are 1, - 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate - surrounding of a power of two. - - The primeForNumBits() function returns the prime associated to a - power of two. For example, primeForNumBits(8) returns 257. -*/ - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - -void QStringHashData::rehashToSize(int size) -{ - short bits = qMax(MinNumBits, (int)numBits); - while (primeForNumBits(bits) < size) bits++; - - if (bits > numBits) - rehashToBits(bits); -} - -void QStringHashData::rehashToBits(short bits) -{ - numBits = qMax(MinNumBits, (int)bits); - - int nb = primeForNumBits(numBits); - if (nb == numBuckets && buckets) - return; - -#ifdef QSTRINGHASH_LINK_DEBUG - if (linkCount) - qFatal("QStringHash: Illegal attempt to rehash a linked hash."); -#endif - - QStringHashNode **newBuckets = new QStringHashNode *[nb]; - ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); - - // Preserve the existing order within buckets so that items with the - // same key will retain the same find/findNext order - for (int i = 0; i < numBuckets; ++i) { - QStringHashNode *bucket = buckets[i]; - if (bucket) - rehashNode(newBuckets, nb, bucket); - } - - delete [] buckets; - buckets = newBuckets; - numBuckets = nb; -} - -void QStringHashData::rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) -{ - QStringHashNode *next = node->next.data(); - if (next) - rehashNode(newBuckets, nb, next); - - int bucket = node->hash % nb; - node->next = newBuckets[bucket]; - newBuckets[bucket] = node; -} +QT_BEGIN_NAMESPACE // Copy of QString's qMemCompare bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) @@ -228,3 +153,4 @@ QString QHashedCStringRef::toUtf16() const return rv; } +QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 2d6c25bdd3..b9f3f81219 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -63,9 +63,6 @@ QT_BEGIN_NAMESPACE -// Enable this to debug hash linking assumptions. -// #define QSTRINGHASH_LINK_DEBUG - class QHashedStringRef; class Q_QML_PRIVATE_EXPORT QHashedString : public QString { @@ -174,854 +171,6 @@ private: mutable quint32 m_hash = 0; }; -class QStringHashData; -class Q_AUTOTEST_EXPORT QStringHashNode -{ -public: - QStringHashNode() - : ckey(nullptr) - { - } - - QStringHashNode(const QHashedString &key) - : length(key.length()), hash(key.hash()), symbolId(0) - { - strData = const_cast<QHashedString &>(key).data_ptr(); - setQString(true); - strData->ref.ref(); - } - - QStringHashNode(const QHashedCStringRef &key) - : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData()) - { - } - - QStringHashNode(const QStringHashNode &o) - : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey) - { - setQString(o.isQString()); - if (isQString()) { strData->ref.ref(); } - } - - ~QStringHashNode() - { - if (isQString()) { if (!strData->ref.deref()) free(strData); } - } - - QFlagPointer<QStringHashNode> next; - - qint32 length = 0; - quint32 hash = 0; - quint32 symbolId = 0; - - union { - const char *ckey; - QStringData *strData; - }; - - inline QHashedString key() const - { - if (isQString()) - return QHashedString(QString((QChar *)strData->data(), length), hash); - - return QHashedString(QString::fromLatin1(ckey, length), hash); - } - - bool isQString() const { return next.flag(); } - void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); } - - inline char *cStrData() const { return (char *)ckey; } - inline quint16 *utf16Data() const { return (quint16 *)strData->data(); } - - inline bool equals(const QV4::Value &string) const { - QString s = string.toQStringNoThrow(); - if (isQString()) { - QStringDataPtr dd; - dd.ptr = strData; - strData->ref.ref(); - return QString(dd) == s; - } else { - return QLatin1String(cStrData(), length) == s; - } - } - - inline bool equals(const QV4::String *string) const { - if (length != string->d()->length() || hash != string->hashValue()) - return false; - if (isQString()) { - QStringDataPtr dd; - dd.ptr = strData; - strData->ref.ref(); - return QString(dd) == string->toQString(); - } else { - return QLatin1String(cStrData(), length) == string->toQString(); - } - } - - inline bool equals(const QHashedStringRef &string) const { - return length == string.length() && - hash == string.hash() && - (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length): - QHashedString::compare(string.constData(), cStrData(), length)); - } - - inline bool equals(const QHashedCStringRef &string) const { - return length == string.length() && - hash == string.hash() && - (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length): - QHashedString::compare(string.constData(), cStrData(), length)); - } -}; - -class Q_AUTOTEST_EXPORT QStringHashData -{ -public: - QStringHashData() {} - - QStringHashNode **buckets = nullptr; - int numBuckets = 0; - int size = 0; - short numBits = 0; -#ifdef QSTRINGHASH_LINK_DEBUG - int linkCount = 0; -#endif - - struct IteratorData { - IteratorData() {} - QStringHashNode *n = nullptr; - void *p = nullptr; - }; - void rehashToBits(short); - void rehashToSize(int); - void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node); - -private: - QStringHashData(const QStringHashData &); - QStringHashData &operator=(const QStringHashData &); -}; - -// For a supplied key type, in what form do we need to keep a hashed version? -template<typename T> -struct HashedForm {}; - -template<> struct HashedForm<QString> { typedef QHashedString Type; }; -template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; }; -template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; }; -template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; }; -template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; }; -template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; }; -template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; }; -template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; }; - -class QStringHashBase -{ -public: - static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);} - static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());} - static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; } - static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; } - static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; } - static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; } - - static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); } - static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; } - - static const QString &toQString(const QString &s) { return s; } - static const QString &toQString(const QHashedString &s) { return s; } - static QString toQString(const QV4::String *s) { return s->toQString(); } - static QString toQString(const QHashedStringRef &s) { return s.toString(); } - - static QString toQString(const QLatin1String &s) { return QString(s); } - static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); } - - static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); } - static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); } - static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); } - - template<typename K> - static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); } -}; - -template<class T> -class QStringHash : public QStringHashBase -{ -public: - typedef QHashedString key_type; - typedef T mapped_type; - - struct Node : public QStringHashNode { - Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {} - Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {} - Node(const Node &o) : QStringHashNode(o), value(o.value) {} - Node() {} - T value; - }; - struct NewedNode : public Node { - NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {} - NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {} - NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {} - NewedNode *nextNewed; - }; - struct ReservedNodePool - { - ReservedNodePool() : nodes(nullptr) {} - ~ReservedNodePool() { delete [] nodes; } - int count = 0; - int used = 0; - Node *nodes; - }; - - QStringHashData data; - NewedNode *newedNodes; - ReservedNodePool *nodePool; - const QStringHash<T> *link; - - template<typename K> - inline Node *findNode(const K &) const; - - inline Node *createNode(const Node &o); - - template<typename K> - inline Node *createNode(const K &, const T &); - - inline Node *insertNode(Node *, quint32); - - inline void initializeNode(Node *, const QHashedString &key); - inline void initializeNode(Node *, const QHashedCStringRef &key); - - template<typename K> - inline Node *takeNode(const K &key, const T &value); - - inline Node *takeNode(const Node &o); - - inline void copy(const QStringHash<T> &); - - void copyNode(const QStringHashNode *otherNode); - - inline QStringHashData::IteratorData iterateFirst() const; - static inline QStringHashData::IteratorData iterateNext(const QStringHashData::IteratorData &); - -public: - inline QStringHash(); - inline QStringHash(const QStringHash &); - inline ~QStringHash(); - - QStringHash &operator=(const QStringHash<T> &); - - void copyAndReserve(const QStringHash<T> &other, int additionalReserve); - void linkAndReserve(const QStringHash<T> &other, int additionalReserve); - - inline bool isEmpty() const; - inline void clear(); - inline int count() const; - - inline int numBuckets() const; - inline bool isLinked() const; - - class ConstIterator { - public: - inline ConstIterator(); - inline ConstIterator(const QStringHashData::IteratorData &); - - inline ConstIterator &operator++(); - - inline bool operator==(const ConstIterator &o) const; - inline bool operator!=(const ConstIterator &o) const; - - template<typename K> - inline bool equals(const K &) const; - - inline QHashedString key() const; - inline const T &value() const; - inline const T &operator*() const; - - inline Node *node() const; - private: - QStringHashData::IteratorData d; - }; - - template<typename K> - inline void insert(const K &, const T &); - - inline void insert(const ConstIterator &); - - template<typename K> - inline T *value(const K &) const; - - inline T *value(const QV4::String *string) const; - inline T *value(const ConstIterator &) const; - - template<typename K> - inline bool contains(const K &) const; - - template<typename K> - inline T &operator[](const K &); - - inline ConstIterator begin() const; - inline ConstIterator end() const; - - inline ConstIterator iterator(Node *n) const; - - template<typename K> - inline ConstIterator find(const K &) const; - - inline void reserve(int); -}; - -template<class T> -QStringHash<T>::QStringHash() -: newedNodes(nullptr), nodePool(nullptr), link(nullptr) -{ -} - -template<class T> -QStringHash<T>::QStringHash(const QStringHash<T> &other) -: newedNodes(nullptr), nodePool(nullptr), link(nullptr) -{ - data.numBits = other.data.numBits; - data.size = other.data.size; - reserve(other.count()); - copy(other); -} - -template<class T> -QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other) -{ - if (&other == this) - return *this; - - clear(); - - data.numBits = other.data.numBits; - data.size = other.data.size; - reserve(other.count()); - copy(other); - - return *this; -} - -template<class T> -void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve) -{ - clear(); - data.numBits = other.data.numBits; - reserve(other.count() + additionalReserve); - copy(other); -} - -template<class T> -void QStringHash<T>::linkAndReserve(const QStringHash<T> &other, int additionalReserve) -{ - clear(); - - if (other.count()) { - data.size = other.data.size; - data.rehashToSize(other.count() + additionalReserve); - - if (data.numBuckets == other.data.numBuckets) { - nodePool = new ReservedNodePool; - nodePool->count = additionalReserve; - nodePool->used = 0; - nodePool->nodes = new Node[additionalReserve]; - -#ifdef QSTRINGHASH_LINK_DEBUG - data.linkCount++; - const_cast<QStringHash<T>&>(other).data.linkCount++; -#endif - - for (int ii = 0; ii < data.numBuckets; ++ii) - data.buckets[ii] = (Node *)other.data.buckets[ii]; - - link = &other; - return; - } - - data.size = 0; - } - - data.numBits = other.data.numBits; - reserve(other.count() + additionalReserve); - copy(other); -} - -template<class T> -QStringHash<T>::~QStringHash() -{ - clear(); -} - -template<class T> -void QStringHash<T>::clear() -{ -#ifdef QSTRINGHASH_LINK_DEBUG - if (link) { - data.linkCount--; - const_cast<QStringHash<T> *>(link)->data.linkCount--; - } - - if (data.linkCount) - qFatal("QStringHash: Illegal attempt to clear a linked hash."); -#endif - - // Delete the individually allocated nodes - NewedNode *n = newedNodes; - while (n) { - NewedNode *c = n; - n = c->nextNewed; - delete c; - } - // Delete the pool allocated nodes - if (nodePool) delete nodePool; - delete [] data.buckets; - - data.buckets = nullptr; - data.numBuckets = 0; - data.numBits = 0; - data.size = 0; - - newedNodes = nullptr; - nodePool = nullptr; - link = nullptr; -} - -template<class T> -bool QStringHash<T>::isEmpty() const -{ - return data.size== 0; -} - -template<class T> -int QStringHash<T>::count() const -{ - return data.size; -} - -template<class T> -int QStringHash<T>::numBuckets() const -{ - return data.numBuckets; -} - -template<class T> -bool QStringHash<T>::isLinked() const -{ - return link != 0; -} - -template<class T> -void QStringHash<T>::initializeNode(Node *node, const QHashedString &key) -{ - node->length = key.length(); - node->hash = key.hash(); - node->strData = const_cast<QHashedString &>(key).data_ptr(); - node->strData->ref.ref(); - node->setQString(true); -} - -template<class T> -void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key) -{ - node->length = key.length(); - node->hash = key.hash(); - node->ckey = key.constData(); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value) -{ - if (nodePool && nodePool->used != nodePool->count) { - Node *rv = nodePool->nodes + nodePool->used++; - initializeNode(rv, hashedString(key)); - rv->value = value; - return rv; - } else { - NewedNode *rv = new NewedNode(hashedString(key), value); - rv->nextNewed = newedNodes; - newedNodes = rv; - return rv; - } -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o) -{ - if (nodePool && nodePool->used != nodePool->count) { - Node *rv = nodePool->nodes + nodePool->used++; - rv->length = o.length; - rv->hash = o.hash; - if (o.isQString()) { - rv->strData = o.strData; - rv->strData->ref.ref(); - rv->setQString(true); - } else { - rv->ckey = o.ckey; - } - rv->symbolId = o.symbolId; - rv->value = o.value; - return rv; - } else { - NewedNode *rv = new NewedNode(o); - rv->nextNewed = newedNodes; - newedNodes = rv; - return rv; - } -} - -template<class T> -void QStringHash<T>::copyNode(const QStringHashNode *otherNode) -{ - // Copy the predecessor before the successor - QStringHashNode *next = otherNode->next.data(); - if (next) - copyNode(next); - - Node *mynode = takeNode(*(const Node *)otherNode); - int bucket = mynode->hash % data.numBuckets; - mynode->next = data.buckets[bucket]; - data.buckets[bucket] = mynode; -} - -template<class T> -void QStringHash<T>::copy(const QStringHash<T> &other) -{ - Q_ASSERT(data.size == 0); - - data.size = other.data.size; - - // Ensure buckets array is created - data.rehashToBits(data.numBits); - - // Preserve the existing order within buckets - for (int i = 0; i < other.data.numBuckets; ++i) { - QStringHashNode *bucket = other.data.buckets[i]; - if (bucket) - copyNode(bucket); - } -} - -template<class T> -QStringHashData::IteratorData -QStringHash<T>::iterateNext(const QStringHashData::IteratorData &d) -{ - QStringHash<T> *This = (QStringHash<T> *)d.p; - Node *node = (Node *)d.n; - - if (This->nodePool && node >= This->nodePool->nodes && - node < (This->nodePool->nodes + This->nodePool->used)) { - node--; - if (node < This->nodePool->nodes) - node = nullptr; - } else { - NewedNode *nn = (NewedNode *)node; - node = nn->nextNewed; - - if (node == nullptr && This->nodePool && This->nodePool->used) - node = This->nodePool->nodes + This->nodePool->used - 1; - } - - if (node == nullptr && This->link) - return This->link->iterateFirst(); - - QStringHashData::IteratorData rv; - rv.n = node; - rv.p = d.p; - return rv; -} - -template<class T> -QStringHashData::IteratorData QStringHash<T>::iterateFirst() const -{ - Node *n = nullptr; - if (newedNodes) - n = newedNodes; - else if (nodePool && nodePool->used) - n = nodePool->nodes + nodePool->used - 1; - - if (n == nullptr && link) - return link->iterateFirst(); - - QStringHashData::IteratorData rv; - rv.n = n; - rv.p = const_cast<QStringHash<T> *>(this); - return rv; -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::iterator(Node *n) const -{ - if (!n) - return ConstIterator(); - - const QStringHash<T> *container = this; - - if (link) { - // This node could be in the linked hash - if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) { - // The node is in this hash - } else if ((n >= link->nodePool->nodes) && (n < (link->nodePool->nodes + link->nodePool->used))) { - // The node is in the linked hash - container = link; - } else { - const NewedNode *ln = link->newedNodes; - while (ln) { - if (ln == n) { - // This node is in the linked hash's newed list - container = link; - break; - } - ln = ln->nextNewed; - } - } - } - - QStringHashData::IteratorData rv; - rv.n = n; - rv.p = const_cast<QStringHash<T> *>(container); - return ConstIterator(rv); -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o) -{ - Node *n = takeNode(o); - return insertNode(n, n->hash); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value) -{ - Node *n = takeNode(key, value); - return insertNode(n, hashOf(key)); -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash) -{ - if (data.size >= data.numBuckets) - data.rehashToBits(data.numBits + 1); - - int bucket = hash % data.numBuckets; - n->next = data.buckets[bucket]; - data.buckets[bucket] = n; - - data.size++; - - return n; -} - -template<class T> -template<class K> -void QStringHash<T>::insert(const K &key, const T &value) -{ - // If this is a linked hash, we can't rely on owning the node, so we always - // create a new one. - Node *n = link?nullptr:findNode(key); - if (n) n->value = value; - else createNode(key, value); -} - -template<class T> -void QStringHash<T>::insert(const ConstIterator &iter) -{ - insert(iter.key(), iter.value()); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const -{ - QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr; - - typename HashedForm<K>::Type hashedKey(hashedString(key)); - while (node && !node->equals(hashedKey)) - node = (*node->next); - - return (Node *)node; -} - -template<class T> -template<class K> -T *QStringHash<T>::value(const K &key) const -{ - Node *n = findNode(key); - return n?&n->value:nullptr; -} - -template<class T> -T *QStringHash<T>::value(const ConstIterator &iter) const -{ - Node *n = iter.node(); - return value(n->key()); -} - -template<class T> -T *QStringHash<T>::value(const QV4::String *string) const -{ - Node *n = findNode(string); - return n?&n->value:nullptr; -} - -template<class T> -template<class K> -bool QStringHash<T>::contains(const K &key) const -{ - return nullptr != value(key); -} - -template<class T> -template<class K> -T &QStringHash<T>::operator[](const K &key) -{ - Node *n = findNode(key); - if (n) return n->value; - else return createNode(key, T())->value; -} - -template<class T> -void QStringHash<T>::reserve(int n) -{ - if (nodePool || 0 == n) - return; - - nodePool = new ReservedNodePool; - nodePool->count = n; - nodePool->used = 0; - nodePool->nodes = new Node[n]; - - data.rehashToSize(n); -} - -template<class T> -QStringHash<T>::ConstIterator::ConstIterator() -{ -} - -template<class T> -QStringHash<T>::ConstIterator::ConstIterator(const QStringHashData::IteratorData &d) -: d(d) -{ -} - -template<class T> -typename QStringHash<T>::ConstIterator &QStringHash<T>::ConstIterator::operator++() -{ - d = QStringHash<T>::iterateNext(d); - return *this; -} - -template<class T> -bool QStringHash<T>::ConstIterator::operator==(const ConstIterator &o) const -{ - return d.n == o.d.n; -} - -template<class T> -bool QStringHash<T>::ConstIterator::operator!=(const ConstIterator &o) const -{ - return d.n != o.d.n; -} - -template<class T> -template<typename K> -bool QStringHash<T>::ConstIterator::equals(const K &key) const -{ - return d.n->equals(key); -} - -template<class T> -QHashedString QStringHash<T>::ConstIterator::key() const -{ - Node *n = (Node *)d.n; - return n->key(); -} -template<class T> -const T &QStringHash<T>::ConstIterator::value() const -{ - Node *n = (Node *)d.n; - return n->value; -} - -template<class T> -const T &QStringHash<T>::ConstIterator::operator*() const -{ - Node *n = (Node *)d.n; - return n->value; -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::ConstIterator::node() const -{ - Node *n = (Node *)d.n; - return n; -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const -{ - return ConstIterator(iterateFirst()); -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::end() const -{ - return ConstIterator(); -} - -template<class T> -template<class K> -typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const -{ - return iterator(findNode(key)); -} - -template<class T> -class QStringMultiHash : public QStringHash<T> -{ -public: - typedef typename QStringHash<T>::ConstIterator ConstIterator; - - template<typename K> - inline void insert(const K &, const T &); - - inline void insert(const ConstIterator &); - - inline ConstIterator findNext(const ConstIterator &) const; -}; - -template<class T> -template<class K> -void QStringMultiHash<T>::insert(const K &key, const T &value) -{ - // Always create a new node - QStringHash<T>::createNode(key, value); -} - -template<class T> -void QStringMultiHash<T>::insert(const ConstIterator &iter) -{ - // Always create a new node - QStringHash<T>::createNode(iter.key(), iter.value()); -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringMultiHash<T>::findNext(const ConstIterator &iter) const -{ - QStringHashNode *node = iter.node(); - if (node) { - QHashedString key(node->key()); - - while ((node = *node->next)) { - if (node->equals(key)) { - return QStringHash<T>::iterator(static_cast<typename QStringHash<T>::Node *>(node)); - } - } - } - - return ConstIterator(); -} - inline uint qHash(const QHashedString &string) { return uint(string.hash()); diff --git a/src/qml/qml/ftw/qlinkedstringhash_p.h b/src/qml/qml/ftw/qlinkedstringhash_p.h new file mode 100644 index 0000000000..67ced7fbbf --- /dev/null +++ b/src/qml/qml/ftw/qlinkedstringhash_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLINKEDSTRINGHASH_P_H +#define QLINKEDSTRINGHASH_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 <private/qstringhash_p.h> + +QT_BEGIN_NAMESPACE + +template<class T> +class QLinkedStringHash : private QStringHash<T> +{ +public: + using typename QStringHash<T>::Node; + using typename QStringHash<T>::NewedNode; + using typename QStringHash<T>::ReservedNodePool; + using typename QStringHash<T>::mapped_type; + + using ConstIteratorData = QStringHashData::IteratorData<const QLinkedStringHash>; + using ConstIterator = typename QStringHash<T>::template Iterator<ConstIteratorData, const T>; + + void linkAndReserve(const QLinkedStringHash<T> &other, int additionalReserve) + { + clear(); + + if (other.count()) { + data.size = other.data.size; + data.rehashToSize(other.count() + additionalReserve); + + if (data.numBuckets == other.data.numBuckets) { + nodePool = new ReservedNodePool; + nodePool->count = additionalReserve; + nodePool->used = 0; + nodePool->nodes = new Node[additionalReserve]; + + for (int ii = 0; ii < data.numBuckets; ++ii) + data.buckets[ii] = (Node *)other.data.buckets[ii]; + + link = &other; + return; + } + + data.size = 0; + } + + data.numBits = other.data.numBits; + reserve(other.count() + additionalReserve); + copy(other); + } + + inline bool isLinked() const + { + return link != 0; + } + + void clear() + { + QStringHash<T>::clear(); + link = nullptr; + } + + template<typename K> + void insert(const K &key, const T &value) + { + // If this is a linked hash, we can't rely on owning the node, so we always + // create a new one. + Node *n = link ? nullptr : QStringHash<T>::findNode(key); + if (n) + n->value = value; + else + QStringHash<T>::createNode(key, value); + } + + template<typename K> + inline ConstIterator find(const K &key) const + { + return iterator(QStringHash<T>::findNode(key)); + } + + ConstIterator begin() const + { + return ConstIterator( + QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>, + ConstIteratorData>(this)); + } + + ConstIterator end() const { return ConstIterator(); } + + inline T *value(const ConstIterator &iter) { return value(iter.node()->key()); } + + using QStringHash<T>::value; + using QStringHash<T>::reserve; + using QStringHash<T>::copy; + +protected: + friend QStringHash<T>; + using QStringHash<T>::data; + using QStringHash<T>::nodePool; + + using QStringHash<T>::createNode; + + inline ConstIteratorData iterateFirst() const + { + const ConstIteratorData rv + = QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>, + ConstIteratorData>(this); + return (rv.n == nullptr && link) ? link->iterateFirst() : rv; + } + + static inline ConstIteratorData iterateNext(const ConstIteratorData &d) + { + const QLinkedStringHash<T> *self = d.p; + const ConstIteratorData rv = QStringHash<T>::iterateNext(d); + return (rv.n == nullptr && self->link) ? self->link->iterateFirst() : rv; + } + + inline ConstIterator iterator(Node *n) const + { + if (!n) + return ConstIterator(); + + const QLinkedStringHash<T> *container = this; + + if (link) { + // This node could be in the linked hash + if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) { + // The node is in this hash + } else if ((n >= link->nodePool->nodes) + && (n < (link->nodePool->nodes + link->nodePool->used))) { + // The node is in the linked hash + container = link; + } else { + const NewedNode *ln = link->newedNodes; + while (ln) { + if (ln == n) { + // This node is in the linked hash's newed list + container = link; + break; + } + ln = ln->nextNewed; + } + } + } + + + ConstIteratorData rv; + rv.n = n; + rv.p = container; + return ConstIterator(rv); + } + + const QLinkedStringHash<T> *link = nullptr; +}; + +template<class T> +class QLinkedStringMultiHash : public QLinkedStringHash<T> +{ +public: + using ConstIterator = typename QLinkedStringHash<T>::ConstIterator; + + template<typename K> + inline void insert(const K &key, const T &value) + { + // Always create a new node + QLinkedStringHash<T>::createNode(key, value); + } + + inline void insert(const ConstIterator &iter) + { + // Always create a new node + QLinkedStringHash<T>::createNode(iter.key(), iter.value()); + } + + inline ConstIterator findNext(const ConstIterator &iter) const + { + if (auto *node = iter.node()) { + QHashedString key(node->key()); + while ((node = static_cast<typename QLinkedStringHash<T>::Node *>(*node->next))) { + if (node->equals(key)) + return QLinkedStringHash<T>::iterator(node); + } + } + + return ConstIterator(); + } +}; + +QT_END_NAMESPACE + +#endif // QLINKEDSTRINGHASH_P_H diff --git a/src/qml/qml/ftw/qprimefornumbits_p.h b/src/qml/qml/ftw/qprimefornumbits_p.h new file mode 100644 index 0000000000..6e9acbf7fd --- /dev/null +++ b/src/qml/qml/ftw/qprimefornumbits_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPRIMEFORNUMBITS_P_H +#define QPRIMEFORNUMBITS_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 <private/qtqmlglobal_p.h> + +QT_BEGIN_NAMESPACE + +/* + The prime_deltas array is a table of selected prime values, even + though it doesn't look like one. The primes we are using are 1, + 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate + surrounding of a power of two. + + The qPrimeForNumBits() function returns the prime associated to a + power of two. For example, qPrimeForNumBits(8) returns 257. +*/ + +inline int qPrimeForNumBits(int numBits) +{ + static constexpr const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 + }; + + return (1 << numBits) + prime_deltas[numBits]; +} + +QT_END_NAMESPACE + +#endif // QPRIMEFORNUMBITS_P_H diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index d32a08e0f5..b4f8acad49 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -60,18 +60,18 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlRefCount { + Q_DISABLE_COPY_MOVE(QQmlRefCount) public: inline QQmlRefCount(); - inline virtual ~QQmlRefCount(); - inline void addref(); - inline void release(); + inline void addref() const; + inline void release() const; inline int count() const; protected: - inline virtual void destroy(); + inline virtual ~QQmlRefCount(); private: - QAtomicInt refCount; + mutable QAtomicInt refCount; }; template<class T> @@ -113,30 +113,25 @@ QQmlRefCount::QQmlRefCount() QQmlRefCount::~QQmlRefCount() { - Q_ASSERT(refCount.load() == 0); + Q_ASSERT(refCount.loadRelaxed() == 0); } -void QQmlRefCount::addref() +void QQmlRefCount::addref() const { - Q_ASSERT(refCount.load() > 0); + Q_ASSERT(refCount.loadRelaxed() > 0); refCount.ref(); } -void QQmlRefCount::release() +void QQmlRefCount::release() const { - Q_ASSERT(refCount.load() > 0); + Q_ASSERT(refCount.loadRelaxed() > 0); if (!refCount.deref()) - destroy(); + delete this; } int QQmlRefCount::count() const { - return refCount.load(); -} - -void QQmlRefCount::destroy() -{ - delete this; + return refCount.loadRelaxed(); } template<class T> diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index e961ed3d0d..7d2ad354d6 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -131,6 +131,9 @@ QQmlThreadPrivate::QQmlThreadPrivate(QQmlThread *q) m_mainThreadWaiting(false), mainSync(nullptr), m_mainObject(this) { setObjectName(QStringLiteral("QQmlThread")); + // This size is aligned with the recursion depth limits in the parser/codegen. In case of + // absurd content we want to hit the recursion checks instead of running out of stack. + setStackSize(8 * 1024 * 1024); } bool QQmlThreadPrivate::event(QEvent *e) diff --git a/src/qml/qml/ftw/qstringhash_p.h b/src/qml/qml/ftw/qstringhash_p.h new file mode 100644 index 0000000000..f9435b4919 --- /dev/null +++ b/src/qml/qml/ftw/qstringhash_p.h @@ -0,0 +1,807 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTRINGHASH_P_H +#define QSTRINGHASH_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 <private/qhashedstring_p.h> +#include <private/qprimefornumbits_p.h> + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QStringHashData; +class QStringHashNode +{ +public: + QStringHashNode() + : ckey(nullptr) + { + } + + QStringHashNode(const QHashedString &key) + : length(key.length()), hash(key.hash()), symbolId(0) + { + strData = const_cast<QHashedString &>(key).data_ptr(); + setQString(true); + strData->ref.ref(); + } + + QStringHashNode(const QHashedCStringRef &key) + : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData()) + { + } + + QStringHashNode(const QStringHashNode &o) + : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey) + { + setQString(o.isQString()); + if (isQString()) { strData->ref.ref(); } + } + + ~QStringHashNode() + { + if (isQString()) { if (!strData->ref.deref()) free(strData); } + } + + QFlagPointer<QStringHashNode> next; + + qint32 length = 0; + quint32 hash = 0; + quint32 symbolId = 0; + + union { + const char *ckey; + QStringData *strData; + }; + + inline QHashedString key() const + { + if (isQString()) + return QHashedString(QString((QChar *)strData->data(), length), hash); + + return QHashedString(QString::fromLatin1(ckey, length), hash); + } + + bool isQString() const { return next.flag(); } + void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); } + + inline char *cStrData() const { return (char *)ckey; } + inline quint16 *utf16Data() const { return (quint16 *)strData->data(); } + + inline bool equals(const QV4::Value &string) const { + QString s = string.toQStringNoThrow(); + if (isQString()) { + QStringDataPtr dd; + dd.ptr = strData; + strData->ref.ref(); + return QString(dd) == s; + } else { + return QLatin1String(cStrData(), length) == s; + } + } + + inline bool equals(const QV4::String *string) const { + if (length != string->d()->length() || hash != string->hashValue()) + return false; + if (isQString()) { + QStringDataPtr dd; + dd.ptr = strData; + strData->ref.ref(); + return QString(dd) == string->toQString(); + } else { + return QLatin1String(cStrData(), length) == string->toQString(); + } + } + + inline bool equals(const QHashedStringRef &string) const { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } + + inline bool equals(const QHashedCStringRef &string) const { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } +}; + +class QStringHashData +{ + Q_DISABLE_COPY_MOVE(QStringHashData) +public: + QStringHashData() = default; + ~QStringHashData() = default; + + /* + A QHash has initially around pow(2, MinNumBits) buckets. For + example, if MinNumBits is 4, it has 17 buckets. + */ + enum { MinNumBits = 4 }; + + QStringHashNode **buckets = nullptr; // life cycle managed by QStringHash + int numBuckets = 0; + int size = 0; + short numBits = 0; + + template<typename StringHash> + struct IteratorData { + IteratorData(QStringHashNode *n = nullptr, StringHash *p = nullptr) : n(n), p(p) {} + + template<typename OtherData> + IteratorData(const OtherData &other) : n(other.n), p(other.p) {} + + QStringHashNode *n; + StringHash *p; + }; + + void rehashToBits(short bits) + { + numBits = qMax(short(MinNumBits), bits); + + int nb = qPrimeForNumBits(numBits); + if (nb == numBuckets && buckets) + return; + + QStringHashNode **newBuckets = new QStringHashNode *[nb]; + ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); + + // Preserve the existing order within buckets so that items with the + // same key will retain the same find/findNext order + for (int i = 0; i < numBuckets; ++i) { + QStringHashNode *bucket = buckets[i]; + if (bucket) + rehashNode(newBuckets, nb, bucket); + } + + delete [] buckets; + buckets = newBuckets; + numBuckets = nb; + } + + void rehashToSize(int size) + { + short bits = qMax(short(MinNumBits), numBits); + while (qPrimeForNumBits(bits) < size) + bits++; + + if (bits > numBits) + rehashToBits(bits); + } + + void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) + { + QStringHashNode *next = node->next.data(); + if (next) + rehashNode(newBuckets, nb, next); + + int bucket = node->hash % nb; + node->next = newBuckets[bucket]; + newBuckets[bucket] = node; + } +}; + +// For a supplied key type, in what form do we need to keep a hashed version? +template<typename T> +struct HashedForm {}; + +template<> struct HashedForm<QString> { typedef QHashedString Type; }; +template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; }; +template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; }; +template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; }; +template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; }; +template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; }; +template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; }; +template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; }; + +class QStringHashBase +{ +public: + static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);} + static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());} + static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; } + static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; } + static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; } + static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; } + + static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); } + static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; } + + static const QString &toQString(const QString &s) { return s; } + static const QString &toQString(const QHashedString &s) { return s; } + static QString toQString(const QV4::String *s) { return s->toQString(); } + static QString toQString(const QHashedStringRef &s) { return s.toString(); } + + static QString toQString(const QLatin1String &s) { return QString(s); } + static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); } + + static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); } + static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); } + static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); } + + template<typename K> + static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); } +}; + +template<class T> +class QStringHash : public QStringHashBase +{ +public: + typedef QHashedString key_type; + typedef T mapped_type; + + using MutableIteratorData = QStringHashData::IteratorData<QStringHash<T>>; + using ConstIteratorData = QStringHashData::IteratorData<const QStringHash<T>>; + + struct Node : public QStringHashNode { + Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {} + Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {} + Node(const Node &o) : QStringHashNode(o), value(o.value) {} + Node() {} + T value; + }; + struct NewedNode : public Node { + NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {} + NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {} + NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {} + NewedNode *nextNewed; + }; + struct ReservedNodePool + { + ReservedNodePool() : nodes(nullptr) {} + ~ReservedNodePool() { delete [] nodes; } + int count = 0; + int used = 0; + Node *nodes; + }; + + QStringHashData data; + NewedNode *newedNodes; + ReservedNodePool *nodePool; + + template<typename K> + inline Node *findNode(const K &) const; + + inline Node *createNode(const Node &o); + + template<typename K> + inline Node *createNode(const K &, const T &); + + inline Node *insertNode(Node *, quint32); + + inline void initializeNode(Node *, const QHashedString &key); + inline void initializeNode(Node *, const QHashedCStringRef &key); + + template<typename K> + inline Node *takeNode(const K &key, const T &value); + + inline Node *takeNode(const Node &o); + + inline void copy(const QStringHash<T> &); + + void copyNode(const QStringHashNode *otherNode); + + template<typename StringHash, typename Data> + static inline Data iterateFirst(StringHash *self); + + template<typename Data> + static inline Data iterateNext(const Data &); + +public: + inline QStringHash(); + inline QStringHash(const QStringHash &); + inline ~QStringHash(); + + QStringHash &operator=(const QStringHash<T> &); + + void copyAndReserve(const QStringHash<T> &other, int additionalReserve); + + inline bool isEmpty() const; + inline void clear(); + inline int count() const; + + inline int numBuckets() const; + + template<typename Data, typename Value> + class Iterator { + public: + inline Iterator() = default; + inline Iterator(const Data &d) : d(d) {} + + inline Iterator &operator++() + { + d = QStringHash<T>::iterateNext(d); + return *this; + } + + inline bool operator==(const Iterator &o) const { return d.n == o.d.n; } + inline bool operator!=(const Iterator &o) const { return d.n != o.d.n; } + + template<typename K> + inline bool equals(const K &key) const { return d.n->equals(key); } + + inline QHashedString key() const { return static_cast<Node *>(d.n)->key(); } + inline Value &value() const { return static_cast<Node *>(d.n)->value; } + inline Value &operator*() const { return static_cast<Node *>(d.n)->value; } + + Node *node() const { return static_cast<Node *>(d.n); } + private: + Data d; + }; + + using MutableIterator = Iterator<MutableIteratorData, T>; + using ConstIterator = Iterator<ConstIteratorData, const T>; + + template<typename K> + inline void insert(const K &, const T &); + inline void insert(const MutableIterator &); + inline void insert(const ConstIterator &); + + template<typename K> + inline T *value(const K &) const; + inline T *value(const QV4::String *string) const; + inline T *value(const MutableIterator &) const; + inline T *value(const ConstIterator &) const; + + template<typename K> + inline bool contains(const K &) const; + + template<typename K> + inline T &operator[](const K &); + + inline MutableIterator begin(); + inline ConstIterator begin() const; + inline ConstIterator constBegin() const { return begin(); } + + inline MutableIterator end(); + inline ConstIterator end() const; + inline ConstIterator constEnd() const { return end(); } + + template<typename K> + inline MutableIterator find(const K &); + + template<typename K> + inline ConstIterator find(const K &) const; + + inline void reserve(int); +}; + +template<class T> +QStringHash<T>::QStringHash() +: newedNodes(nullptr), nodePool(nullptr) +{ +} + +template<class T> +QStringHash<T>::QStringHash(const QStringHash<T> &other) +: newedNodes(nullptr), nodePool(nullptr) +{ + data.numBits = other.data.numBits; + data.size = other.data.size; + reserve(other.count()); + copy(other); +} + +template<class T> +QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other) +{ + if (&other == this) + return *this; + + clear(); + + data.numBits = other.data.numBits; + data.size = other.data.size; + reserve(other.count()); + copy(other); + + return *this; +} + +template<class T> +void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve) +{ + clear(); + data.numBits = other.data.numBits; + reserve(other.count() + additionalReserve); + copy(other); +} + +template<class T> +QStringHash<T>::~QStringHash() +{ + clear(); +} + +template<class T> +void QStringHash<T>::clear() +{ + // Delete the individually allocated nodes + NewedNode *n = newedNodes; + while (n) { + NewedNode *c = n; + n = c->nextNewed; + delete c; + } + // Delete the pool allocated nodes + if (nodePool) delete nodePool; + delete [] data.buckets; + + data.buckets = nullptr; + data.numBuckets = 0; + data.numBits = 0; + data.size = 0; + + newedNodes = nullptr; + nodePool = nullptr; +} + +template<class T> +bool QStringHash<T>::isEmpty() const +{ + return data.size== 0; +} + +template<class T> +int QStringHash<T>::count() const +{ + return data.size; +} + +template<class T> +int QStringHash<T>::numBuckets() const +{ + return data.numBuckets; +} + +template<class T> +void QStringHash<T>::initializeNode(Node *node, const QHashedString &key) +{ + node->length = key.length(); + node->hash = key.hash(); + node->strData = const_cast<QHashedString &>(key).data_ptr(); + node->strData->ref.ref(); + node->setQString(true); +} + +template<class T> +void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key) +{ + node->length = key.length(); + node->hash = key.hash(); + node->ckey = key.constData(); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + initializeNode(rv, hashedString(key)); + rv->value = value; + return rv; + } else { + NewedNode *rv = new NewedNode(hashedString(key), value); + rv->nextNewed = newedNodes; + newedNodes = rv; + return rv; + } +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + rv->length = o.length; + rv->hash = o.hash; + if (o.isQString()) { + rv->strData = o.strData; + rv->strData->ref.ref(); + rv->setQString(true); + } else { + rv->ckey = o.ckey; + } + rv->symbolId = o.symbolId; + rv->value = o.value; + return rv; + } else { + NewedNode *rv = new NewedNode(o); + rv->nextNewed = newedNodes; + newedNodes = rv; + return rv; + } +} + +template<class T> +void QStringHash<T>::copyNode(const QStringHashNode *otherNode) +{ + // Copy the predecessor before the successor + QStringHashNode *next = otherNode->next.data(); + if (next) + copyNode(next); + + Node *mynode = takeNode(*(const Node *)otherNode); + int bucket = mynode->hash % data.numBuckets; + mynode->next = data.buckets[bucket]; + data.buckets[bucket] = mynode; +} + +template<class T> +void QStringHash<T>::copy(const QStringHash<T> &other) +{ + Q_ASSERT(data.size == 0); + + data.size = other.data.size; + + // Ensure buckets array is created + data.rehashToBits(data.numBits); + + // Preserve the existing order within buckets + for (int i = 0; i < other.data.numBuckets; ++i) { + QStringHashNode *bucket = other.data.buckets[i]; + if (bucket) + copyNode(bucket); + } +} + +template<class T> +template<typename Data> +Data QStringHash<T>::iterateNext(const Data &d) +{ + auto *This = d.p; + Node *node = (Node *)d.n; + + if (This->nodePool && node >= This->nodePool->nodes && + node < (This->nodePool->nodes + This->nodePool->used)) { + node--; + if (node < This->nodePool->nodes) + node = nullptr; + } else { + NewedNode *nn = (NewedNode *)node; + node = nn->nextNewed; + + if (node == nullptr && This->nodePool && This->nodePool->used) + node = This->nodePool->nodes + This->nodePool->used - 1; + } + + Data rv; + rv.n = node; + rv.p = d.p; + return rv; +} + +template<class T> +template<typename StringHash, typename Data> +Data QStringHash<T>::iterateFirst(StringHash *self) +{ + typename StringHash::Node *n = nullptr; + if (self->newedNodes) + n = self->newedNodes; + else if (self->nodePool && self->nodePool->used) + n = self->nodePool->nodes + self->nodePool->used - 1; + + Data rv; + rv.n = n; + rv.p = self; + return rv; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o) +{ + Node *n = takeNode(o); + return insertNode(n, n->hash); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value) +{ + Node *n = takeNode(key, value); + return insertNode(n, hashOf(key)); +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash) +{ + if (data.size >= data.numBuckets) + data.rehashToBits(data.numBits + 1); + + int bucket = hash % data.numBuckets; + n->next = data.buckets[bucket]; + data.buckets[bucket] = n; + + data.size++; + + return n; +} + +template<class T> +template<class K> +void QStringHash<T>::insert(const K &key, const T &value) +{ + Node *n = findNode(key); + if (n) + n->value = value; + else + createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const MutableIterator &iter) +{ + insert(iter.key(), iter.value()); +} + +template<class T> +void QStringHash<T>::insert(const ConstIterator &iter) +{ + insert(iter.key(), iter.value()); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const +{ + QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr; + + typename HashedForm<K>::Type hashedKey(hashedString(key)); + while (node && !node->equals(hashedKey)) + node = (*node->next); + + return (Node *)node; +} + +template<class T> +template<class K> +T *QStringHash<T>::value(const K &key) const +{ + Node *n = findNode(key); + return n?&n->value:nullptr; +} + +template<typename T> +T *QStringHash<T>::value(const MutableIterator &iter) const +{ + return value(iter.node()->key()); +} + +template<class T> +T *QStringHash<T>::value(const ConstIterator &iter) const +{ + return value(iter.node()->key()); +} + +template<class T> +T *QStringHash<T>::value(const QV4::String *string) const +{ + Node *n = findNode(string); + return n?&n->value:nullptr; +} + +template<class T> +template<class K> +bool QStringHash<T>::contains(const K &key) const +{ + return nullptr != value(key); +} + +template<class T> +template<class K> +T &QStringHash<T>::operator[](const K &key) +{ + Node *n = findNode(key); + if (n) return n->value; + else return createNode(key, T())->value; +} + +template<class T> +void QStringHash<T>::reserve(int n) +{ + if (nodePool || 0 == n) + return; + + nodePool = new ReservedNodePool; + nodePool->count = n; + nodePool->used = 0; + nodePool->nodes = new Node[n]; + + data.rehashToSize(n); +} + +template<class T> +typename QStringHash<T>::MutableIterator QStringHash<T>::begin() +{ + return MutableIterator(iterateFirst<QStringHash<T>, MutableIteratorData>(this)); +} + +template<class T> +typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const +{ + return ConstIterator(iterateFirst<const QStringHash<T>, ConstIteratorData>(this)); +} + +template<class T> +typename QStringHash<T>::MutableIterator QStringHash<T>::end() +{ + return MutableIterator(); +} + +template<class T> +typename QStringHash<T>::ConstIterator QStringHash<T>::end() const +{ + return ConstIterator(); +} + +template<class T> +template<class K> +typename QStringHash<T>::MutableIterator QStringHash<T>::find(const K &key) +{ + Node *n = findNode(key); + return n ? MutableIterator(MutableIteratorData(n, this)) : MutableIterator(); +} + +template<class T> +template<class K> +typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const +{ + Node *n = findNode(key); + return n ? ConstIterator(ConstIteratorData(n, this)) : ConstIterator(); +} + +QT_END_NAMESPACE + +#endif // QSTRINGHASH_P_H diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index ca13ce9211..2e9c6f3de6 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,5 +1,14 @@ SOURCES += \ + $$PWD/qqml.cpp \ + $$PWD/qqmldatablob.cpp \ + $$PWD/qqmldirdata.cpp \ + $$PWD/qqmlerror.cpp \ $$PWD/qqmlopenmetaobject.cpp \ + $$PWD/qqmlscriptblob.cpp \ + $$PWD/qqmlscriptdata.cpp \ + $$PWD/qqmltypedata.cpp \ + $$PWD/qqmltypeloaderqmldircontent.cpp \ + $$PWD/qqmltypeloaderthread.cpp \ $$PWD/qqmlvmemetaobject.cpp \ $$PWD/qqmlengine.cpp \ $$PWD/qqmlexpression.cpp \ @@ -14,14 +23,21 @@ SOURCES += \ $$PWD/qqmlvme.cpp \ $$PWD/qqmlboundsignal.cpp \ $$PWD/qqmlmetatype.cpp \ + $$PWD/qqmlmetatypedata.cpp \ $$PWD/qqmlstringconverters.cpp \ + $$PWD/qqmltype.cpp \ + $$PWD/qqmltypemodule.cpp \ + $$PWD/qqmltypemoduleversion.cpp \ $$PWD/qqmlparserstatus.cpp \ $$PWD/qqmltypeloader.cpp \ $$PWD/qqmlinfo.cpp \ $$PWD/qqmlvaluetype.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ + $$PWD/qqmlmetaobject.cpp \ $$PWD/qqmlnotifier.cpp \ + $$PWD/qqmlobjectorgadget.cpp \ + $$PWD/qqmlstaticmetaobject.cpp \ $$PWD/qqmltypenotavailable.cpp \ $$PWD/qqmltypenamecache.cpp \ $$PWD/qqmlscriptstring.cpp \ @@ -44,13 +60,26 @@ SOURCES += \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ $$PWD/qqmldelayedcallqueue.cpp \ - $$PWD/qqmlloggingcategory.cpp + $$PWD/qqmlloggingcategory.cpp \ + $$PWD/qqmlirloader.cpp \ + $$PWD/qqmlpropertyresolver.cpp \ + $$PWD/qqmltypecompiler.cpp \ + $$PWD/qqmlpropertycachecreator.cpp \ + $$PWD/qqmlpropertyvalidator.cpp HEADERS += \ + $$PWD/qqmldatablob_p.h \ + $$PWD/qqmldirdata_p.h \ $$PWD/qqmlglobal_p.h \ $$PWD/qqmlopenmetaobject_p.h \ + $$PWD/qqmlscriptblob_p.h \ + $$PWD/qqmlscriptdata_p.h \ + $$PWD/qqmltypedata_p.h \ + $$PWD/qqmltypeloaderqmldircontent_p.h \ + $$PWD/qqmltypeloaderthread_p.h \ $$PWD/qqmlvmemetaobject_p.h \ $$PWD/qqml.h \ + $$PWD/qqmlerror.h \ $$PWD/qqmlproperty.h \ $$PWD/qqmlcomponent.h \ $$PWD/qqmlcomponent_p.h \ @@ -68,6 +97,12 @@ HEADERS += \ $$PWD/qqmlexpression_p.h \ $$PWD/qqmlprivate.h \ $$PWD/qqmlmetatype_p.h \ + $$PWD/qqmlmetatypedata_p.h \ + $$PWD/qqmltype_p.h \ + $$PWD/qqmltype_p_p.h \ + $$PWD/qqmltypemodule_p.h \ + $$PWD/qqmltypemodule_p_p.h \ + $$PWD/qqmltypemoduleversion_p.h \ $$PWD/qqmlengine.h \ $$PWD/qqmlcontext.h \ $$PWD/qqmlexpression.h \ @@ -81,9 +116,17 @@ HEADERS += \ $$PWD/qqmldata_p.h \ $$PWD/qqmlvaluetype_p.h \ $$PWD/qqmlcleanup_p.h \ + $$PWD/qqmlenumdata_p.h \ + $$PWD/qqmlenumvalue_p.h \ $$PWD/qqmlpropertycache_p.h \ + $$PWD/qqmlpropertycachemethodarguments_p.h \ + $$PWD/qqmlpropertycachevector_p.h \ + $$PWD/qqmlpropertydata_p.h \ $$PWD/qqmlpropertyindex_p.h \ + $$PWD/qqmlmetaobject_p.h \ $$PWD/qqmlnotifier_p.h \ + $$PWD/qqmlobjectorgadget_p.h \ + $$PWD/qqmlstaticmetaobject_p.h \ $$PWD/qqmltypenotavailable_p.h \ $$PWD/qqmltypenamecache_p.h \ $$PWD/qqmlscriptstring.h \ @@ -110,7 +153,13 @@ HEADERS += \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ $$PWD/qqmldelayedcallqueue_p.h \ - $$PWD/qqmlloggingcategory_p.h + $$PWD/qqmlloggingcategory_p.h \ + $$PWD/qqmlirloader_p.h \ + $$PWD/qqmlpropertyresolver_p.h \ + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qqmlpropertycachecreator_p.h \ + $$PWD/qqmlpropertyvalidator_p.h \ + $$PWD/qqmlsourcecoordinate_p.h qtConfig(qml-xml-http-request) { HEADERS += \ @@ -129,5 +178,15 @@ qtConfig(qml-locale) { $$PWD/qqmllocale.cpp } +qtConfig(qml-network) { + HEADERS += \ + $$PWD/qqmltypeloadernetworkreplyproxy_p.h + + SOURCES += \ + $$PWD/qqmltypeloadernetworkreplyproxy.cpp +} + +android: DEFINES += LIBS_SUFFIX='\\"_$${QT_ARCH}.so\\"' + include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp new file mode 100644 index 0000000000..5b16a3c9e2 --- /dev/null +++ b/src/qml/qml/qqml.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqml.h" + +#include <QtQml/qqmlprivate.h> + +#include <private/qqmlengine_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmltype_p_p.h> +#include <private/qqmltypemodule_p_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +void qmlClearTypeRegistrations() // Declared in qqml.h +{ + QQmlMetaType::clearTypeRegistrations(); + QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types + qmlClearEnginePlugins(); +} + +//From qqml.h +bool qmlProtectModule(const char *uri, int majVersion) +{ + return QQmlMetaType::protectModule(uri, majVersion); +} + +//From qqml.h +void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) +{ + QQmlMetaType::registerModule(uri, versionMajor, versionMinor); +} + +//From qqml.h +int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName); +} + +// From qqmlprivate.h +QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *) +{ + if (!m_object) { + QQmlError error; + error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine.")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + + if (qeng->thread() != m_object->thread()) { + QQmlError error; + error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + if (alreadyCalled) { + QQmlError error; + error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + alreadyCalled = true; + qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership); + return m_object; +}; + +/* +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) +{ + if (type == AutoParentRegistration) { + return QQmlMetaType::registerAutoParentFunction( + *reinterpret_cast<RegisterAutoParent *>(data)); + } else if (type == QmlUnitCacheHookRegistration) { + return QQmlMetaType::registerUnitCacheHook( + *reinterpret_cast<RegisterQmlUnitCacheHook *>(data)); + } + + QQmlType dtype; + if (type == TypeRegistration) + dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)); + else if (type == InterfaceRegistration) + dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)); + else if (type == SingletonRegistration) + dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data)); + else if (type == CompositeRegistration) + dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data)); + else if (type == CompositeSingletonRegistration) + dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data)); + else + return -1; + + if (!dtype.isValid()) + return -1; + + QQmlMetaType::registerUndeletableType(dtype); + return dtype.index(); +} + +void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data) +{ + switch (type) { + case AutoParentRegistration: + QQmlMetaType::unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data)); + break; + case QmlUnitCacheHookRegistration: + QQmlMetaType::removeCachedUnitLookupFunction( + reinterpret_cast<QmlUnitCacheLookupFunction>(data)); + break; + case TypeRegistration: + case InterfaceRegistration: + case SingletonRegistration: + case CompositeRegistration: + case CompositeSingletonRegistration: + QQmlMetaType::unregisterType(data); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 6bd66464b5..b2bf1f328f 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor; void Q_QML_EXPORT qmlClearTypeRegistrations(); template<typename T> -int qmlRegisterType() +int qmlRegisterAnonymousType(const char *uri, int versionMajor) { QML_GETTYPENAMES @@ -115,7 +115,7 @@ int qmlRegisterType() nullptr, QString(), - nullptr, 0, 0, nullptr, &T::staticMetaObject, + uri, versionMajor, 0, nullptr, &T::staticMetaObject, QQmlPrivate::attachedPropertiesFunc<T>(), QQmlPrivate::attachedPropertiesMetaObject<T>(), @@ -133,6 +133,14 @@ int qmlRegisterType() return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); } +#if QT_DEPRECATED_SINCE(5, 14) +template<typename T> +QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType() +{ + return qmlRegisterAnonymousType<T>("", 1); +} +#endif + int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); template<typename T> @@ -579,9 +587,13 @@ namespace QtQml { Q_QML_EXPORT void qmlExecuteDeferred(QObject *); Q_QML_EXPORT QQmlContext *qmlContext(const QObject *); Q_QML_EXPORT QQmlEngine *qmlEngine(const QObject *); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *, const QObject *, - const QMetaObject *, bool create); +#if QT_DEPRECATED_SINCE(5, 14) + Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool") + QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); + Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool") + QObject *qmlAttachedPropertiesObject( + int *, const QObject *, const QMetaObject *, bool create); +#endif Q_QML_EXPORT QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *, const QMetaObject *); Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc func, @@ -614,8 +626,6 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create); } -Q_QML_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor); - inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*callback)(QQmlEngine *, QJSEngine *)) { @@ -624,13 +634,13 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi uri, versionMajor, versionMinor, typeName, - callback, nullptr, nullptr, 0, 0 + callback, nullptr, nullptr, 0, 0, {} }; return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } -enum { QmlCurrentSingletonTypeRegistrationVersion = 2 }; +enum { QmlCurrentSingletonTypeRegistrationVersion = 3 }; template <typename T> inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*callback)(QQmlEngine *, QJSEngine *)) @@ -642,12 +652,40 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi uri, versionMajor, versionMinor, typeName, - nullptr, callback, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0 + nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback }; return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } +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) +{ + + QML_GETTYPENAMES + + QQmlPrivate::RegisterSingletonType api = { + QmlCurrentSingletonTypeRegistrationVersion, + + uri, versionMajor, versionMinor, typeName, + + nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); +} + +template<typename T> +inline auto qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, + const char *typeName, T *cppObject) -> typename std::enable_if<std::is_base_of<QObject, T>::value, int>::type +{ + QQmlPrivate::RegisterSingletonFunctor registrationFunctor; + registrationFunctor.m_object = cppObject; + return qmlRegisterSingletonType<T>(uri, versionMajor, versionMinor, typeName, registrationFunctor); +} + inline int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) { if (url.isRelative()) { diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index facd79d211..adb036e2d0 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include <QtQml/qqmlfile.h> #include <QtCore/QCoreApplication> #include <QtCore/QTranslator> #include <QQmlComponent> @@ -62,9 +63,6 @@ void QQmlApplicationEnginePrivate::cleanUp() obj->disconnect(q); qDeleteAll(objects); -#if QT_CONFIG(translation) - qDeleteAll(translators); -#endif } void QQmlApplicationEnginePrivate::init() @@ -75,10 +73,11 @@ void QQmlApplicationEnginePrivate::init() q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit, Qt::QueuedConnection); #if QT_CONFIG(translation) - QTranslator* qtTranslator = new QTranslator; + QTranslator* qtTranslator = new QTranslator(q); if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm"))) QCoreApplication::installTranslator(qtTranslator); - translators << qtTranslator; + else + delete qtTranslator; #endif new QQmlFileSelector(q,q); QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true)); @@ -92,13 +91,12 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile)); - QTranslator *translator = new QTranslator; - if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) { + Q_Q(QQmlApplicationEngine); + QTranslator *translator = new QTranslator(q); + if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) QCoreApplication::installTranslator(translator); - translators << translator; - } else { + else delete translator; - } #else Q_UNUSED(rootFile) #endif @@ -133,7 +131,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) q->objectCreated(nullptr, c->url()); break; case QQmlComponent::Ready: { - auto newObj = c->create(); + auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties); objects << newObj; QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); }); q->objectCreated(objects.constLast(), c->url()); @@ -281,6 +279,22 @@ void QQmlApplicationEngine::load(const QString &filePath) } /*! + Sets the \a initialProperties with which the QML component gets initialized after + it gets loaded. + + + \sa QQmlComponent::setInitialProperties + \sa QQmlApplicationEngine::load + \sa QQmlApplicationEngine::loadData + \since 5.14 +*/ +void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties) +{ + Q_D(QQmlApplicationEngine); + d->initialProperties = initialProperties; +} + +/*! Loads the QML given in \a data. The object tree defined by \a data is instantiated immediately. diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index bb5d6b5d68..2b4de91154 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -66,6 +66,7 @@ public: public Q_SLOTS: void load(const QUrl &url); void load(const QString &filePath); + void setInitialProperties(const QVariantMap &initialProperties); void loadData(const QByteArray &data, const QUrl &url = QUrl()); Q_SIGNALS: diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 6cf6828832..1279e400e8 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE -class QTranslator; class QFileSelector; class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate { @@ -74,10 +73,7 @@ public: void loadTranslations(const QUrl &rootFile); void finishLoad(QQmlComponent *component); QList<QObject *> objects; - -#if QT_CONFIG(translation) - QList<QTranslator *> translators; -#endif + QVariantMap initialProperties; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b164517011..52572be2b2 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -49,6 +49,7 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qv4variantobject_p.h> #include <private/qv4jscall_p.h> @@ -297,8 +298,9 @@ protected: case QMetaType::Int: if (result.isInteger()) return doStore<int>(result.integerValue(), pd, flags); - else if (result.isNumber()) - return doStore<int>(result.doubleValue(), pd, flags); + else if (result.isNumber()) { + return doStore<int>(QV4::StaticValue::toInteger(result.doubleValue()), pd, flags); + } break; case QMetaType::Double: if (result.isNumber()) @@ -335,7 +337,7 @@ protected: class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> { public: - QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) + QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) { setCompilationUnit(compilationUnit); m_binding = binding; @@ -356,7 +358,7 @@ public: if (!isAddedToObject() || hasError()) return; - const QString result = m_binding->valueAsString(m_compilationUnit.data()); + const QString result = m_compilationUnit->bindingValueAsString(m_binding); Q_ASSERT(targetObject()); @@ -378,7 +380,7 @@ private: const QV4::CompiledData::Binding *m_binding; }; -QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) +QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) { QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding); @@ -517,9 +519,9 @@ QString QQmlBinding::expressionIdentifier() const { if (auto f = function()) { QString url = f->sourceFile(); - quint16 lineNumber = f->compiledFunction->location.line; - quint16 columnNumber = f->compiledFunction->location.column; - return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); + uint lineNumber = f->compiledFunction->location.line; + uint columnNumber = f->compiledFunction->location.column; + return url + QString::asprintf(":%u:%u", lineNumber, columnNumber); } return QStringLiteral("[native code]"); diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index f192de4342..7f96b4df9f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -63,6 +63,7 @@ #include <private/qqmlabstractbinding_p.h> #include <private/qqmljavascriptexpression_p.h> +#include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -79,7 +80,7 @@ public: const QString &url = QString(), quint16 lineNumber = 0); static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function, QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope); - static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, + static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt); ~QQmlBinding() override; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index e5b78591e0..ff01e737ca 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -44,7 +44,6 @@ #include "qqmlengine_p.h" #include "qqmlexpression_p.h" #include "qqmlcontext_p.h" -#include "qqmlmetatype_p.h" #include "qqml.h" #include "qqmlcontext.h" #include "qqmlglobal_p.h" @@ -57,6 +56,7 @@ #include <private/qv4value_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> @@ -210,8 +210,6 @@ void QQmlBoundSignalExpression::evaluate(void **a) } else if (type == QMetaType::Int) { //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise jsCall->args[ii] = QV4::Value::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); } else if (ep->isQObject(type)) { if (!*reinterpret_cast<void* const *>(a[ii + 1])) jsCall->args[ii] = QV4::Value::nullValue(); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 7ad9310160..b26b90d2aa 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -51,8 +51,6 @@ #include "qqmlincubator_p.h" #include <private/qqmljavascriptexpression_p.h> -#include <private/qv8engine_p.h> - #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> @@ -66,7 +64,6 @@ #include <QThreadStorage> #include <QtCore/qdebug.h> #include <qqmlinfo.h> -#include "qqmlmemoryprofiler_p.h" namespace { QThreadStorage<int> creationDepth; @@ -74,7 +71,7 @@ namespace { QT_BEGIN_NAMESPACE -class QQmlComponentExtension : public QV8Engine::Deletable +class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable { public: QQmlComponentExtension(QV4::ExecutionEngine *v4); @@ -353,6 +350,32 @@ void QQmlComponentPrivate::clear() compilationUnit = nullptr; } +QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context) +{ + if (!engine) { + // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check + qWarning("QQmlComponent: Must provide an engine before calling create"); + return nullptr; + } + if (!context) + context = engine->rootContext(); + return q->beginCreate(context); +} + +bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value) +{ + QQmlProperty prop(component, name); + auto privProp = QQmlPropertyPrivate::get(prop); + if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) { + QQmlError error{}; + error.setUrl(url); + error.setDescription(QLatin1String("Could not set property %1").arg(name)); + state.errors.push_back(error); + return false; + } else + return true; +} + /*! \internal */ @@ -552,7 +575,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, /*! \internal */ -QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent) +QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit, + int start, QObject *parent) : QQmlComponent(engine, parent) { Q_D(QQmlComponent); @@ -781,20 +805,30 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) QObject *QQmlComponent::create(QQmlContext *context) { Q_D(QQmlComponent); - QML_MEMORY_SCOPE_URL(url()); - if (!d->engine) { - // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check - qWarning("QQmlComponent: Must provide an engine before calling create"); - return nullptr; - } + QObject *rv = d->doBeginCreate(this, context); + if (rv) + completeCreate(); + return rv; +} - if (!context) - context = d->engine->rootContext(); +/*! + Create an object instance of this component, and initialize its toplevel + properties with \a initialProperties. \a context specifies the context + where the object instance is to be created. - QObject *rv = beginCreate(context); - if (rv) + \sa QQmlComponent::create + \since 5.14 +*/ +QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context) +{ + Q_D(QQmlComponent); + + QObject *rv = d->doBeginCreate(this, context); + if (rv) { + setInitialProperties(rv, initialProperties); completeCreate(); + } return rv; } @@ -1070,6 +1104,26 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, enginePriv->incubate(incubator, forContextData); } +/*! + Set toplevel \a properties of the \a component. + + + This method provides advanced control over component instance creation. + In general, programmers should use + \l QQmlComponent::createWithInitialProperties to create a component. + + Use this method after beginCreate and before completeCreate has been called. + If a provided property does not exist, a warning is issued. + + \since 5.14 +*/ +void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties) +{ + Q_D(QQmlComponent); + for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) + d->setInitialProperty(component, it.key(), it.value()); +} + /* This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData arguments instead of QQmlContext which means we don't have to construct the rather weighty diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 20199d0b21..f259c99b08 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -59,9 +59,7 @@ class QQmlComponentPrivate; class QQmlComponentAttached; namespace QV4 { -namespace CompiledData { -struct CompilationUnit; -} +class ExecutableCompilationUnit; } class Q_QML_EXPORT QQmlComponent : public QObject @@ -102,6 +100,8 @@ public: QUrl url() const; virtual QObject *create(QQmlContext *context = nullptr); + QObject *createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context = nullptr); + void setInitialProperties(QObject *component, const QVariantMap &properties); virtual QObject *beginCreate(QQmlContext *); virtual void completeCreate(); @@ -128,7 +128,8 @@ protected: Q_INVOKABLE void incubateObject(QQmlV4Function *); private: - QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent); + QQmlComponent(QQmlEngine *, QV4::ExecutableCompilationUnit *compilationUnit, int, + QObject *parent); Q_DISABLE_COPY(QQmlComponent) friend class QQmlTypeData; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 4d9e4c6c15..2170646b89 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -60,6 +60,7 @@ #include "qqmlerror.h" #include "qqml.h" #include <private/qqmlobjectcreator_p.h> +#include <private/qqmltypedata_p.h> #include <QtCore/QString> #include <QtCore/QStringList> @@ -105,7 +106,7 @@ public: qreal progress; int start; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; struct ConstructionState { ConstructionState() @@ -142,6 +143,9 @@ public: static QQmlComponentPrivate *get(QQmlComponent *c) { return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(c)); } + + QObject *doBeginCreate(QQmlComponent *q, QQmlContext *context); + bool setInitialProperty(QObject *component, const QString &name, const QVariant& value); }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 3710cee162..f75a076bcb 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -317,6 +317,12 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value) d->propertyValues[idx] = value; QMetaObject::activate(this, d->notifyIndex, idx, nullptr); } + + if (auto *obj = qvariant_cast<QObject *>(value)) { + connect(obj, &QObject::destroyed, this, [d, name](QObject *destroyed) { + d->dropDestroyedQObject(name, destroyed); + }); + } } /*! @@ -352,7 +358,7 @@ void QQmlContext::setContextProperties(const QVector<PropertyPair> &properties) data->expressions = nullptr; data->childContexts = nullptr; - for (auto property : properties) + for (const auto &property : properties) setContextProperty(property.name, property.value); data->expressions = expressions; @@ -438,23 +444,20 @@ QUrl QQmlContext::resolvedUrl(const QUrl &src) QUrl QQmlContextData::resolvedUrl(const QUrl &src) { - QQmlContextData *ctxt = this; - QUrl resolved; if (src.isRelative() && !src.isEmpty()) { - if (ctxt) { - while(ctxt) { - if (ctxt->url().isValid()) - break; - else - ctxt = ctxt->parent; - } - - if (ctxt) - resolved = ctxt->url().resolved(src); - else if (engine) - resolved = engine->baseUrl().resolved(src); - } + QQmlContextData *ctxt = this; + do { + if (ctxt->url().isValid()) + break; + else + ctxt = ctxt->parent; + } while (ctxt); + + if (ctxt) + resolved = ctxt->url().resolved(src); + else if (engine) + resolved = engine->baseUrl().resolved(src); } else { resolved = src; } @@ -527,6 +530,20 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind } } +void QQmlContextPrivate::dropDestroyedQObject(const QString &name, QObject *destroyed) +{ + if (!data->isValid()) + return; + + const int idx = data->propertyNames().value(name); + Q_ASSERT(idx >= 0); + if (qvariant_cast<QObject *>(propertyValues[idx]) != destroyed) + return; + + propertyValues[idx] = QVariant::fromValue<QObject *>(nullptr); + QMetaObject::activate(q_func(), notifyIndex, idx, nullptr); +} + QQmlContextData::QQmlContextData() : QQmlContextData(nullptr) @@ -845,7 +862,7 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() return QQmlContextPrivate::get(asQQmlContext()); } -void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex) +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) { typeCompilationUnit = unit; componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index 7e3cef8e1d..5f7316b00c 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -66,7 +66,7 @@ #include <private/qflagpointer_p.h> #include <private/qqmlguard_p.h> -#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <private/qv4identifier_p.h> QT_BEGIN_NAMESPACE @@ -104,6 +104,8 @@ public: static int context_count(QQmlListProperty<QObject> *); static QObject *context_at(QQmlListProperty<QObject> *, int); + + void dropDestroyedQObject(const QString &name, QObject *destroyed); }; class QQmlComponentAttached; @@ -152,12 +154,12 @@ public: QQmlIncubatorPrivate *incubator; // Compilation unit for contexts that belong to a compiled type. - QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit; // object index in CompiledData::Unit to component that created this context int componentObjectIndex; - void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex); + void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex); // flag indicates whether the context owns the cache (after mutation) or not. mutable QV4::IdentifierHash propertyNameCache; diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 5cf87f5264..a5f34dafdf 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -39,7 +39,7 @@ #include "qqmlcustomparser_p.h" -#include <private/qqmltypecompiler_p.h> +#include <private/qv4compileddata_p.h> #include <QtCore/qdebug.h> @@ -100,7 +100,12 @@ void QQmlCustomParser::clearErrors() */ void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description) { - exceptions << QQmlCompileError(location, description); + QQmlJS::DiagnosticMessage error; + error.line = location.line; + error.column = location.column; + error.message = description; + + exceptions << error; } struct StaticQtMetaObject : public QObject diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index bf28bca447..df8cbc9072 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -51,10 +51,9 @@ // We mean it. // -#include "qqmlmetatype_p.h" #include "qqmlerror.h" #include "qqmlbinding_p.h" -#include <private/qqmltypecompiler_p.h> +#include <private/qv4compileddata_p.h> #include <QtCore/qbytearray.h> #include <QtCore/qxmlstream.h> @@ -81,10 +80,10 @@ public: void clearErrors(); Flags flags() const { return m_flags; } - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; - QVector<QQmlCompileError> errors() const { return exceptions; } + QVector<QQmlJS::DiagnosticMessage> errors() const { return exceptions; } protected: void error(const QV4::CompiledData::Binding *binding, const QString& description) @@ -98,7 +97,7 @@ protected: const QMetaObject *resolveType(const QString&) const; private: - QVector<QQmlCompileError> exceptions; + QVector<QQmlJS::DiagnosticMessage> exceptions; QQmlEnginePrivate *engine; const QQmlPropertyValidator *validator; Flags m_flags; diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index f4c03fc17c..299476f5c8 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -76,8 +76,8 @@ class QQmlDataExtended; class QQmlNotifierEndpoint; namespace QV4 { +class ExecutableCompilationUnit; namespace CompiledData { -struct CompilationUnit; struct Binding; } } @@ -226,14 +226,14 @@ public: ~DeferredData(); unsigned int deferredIdx; QMultiHash<int, const QV4::CompiledData::Binding *> bindings; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;//Not always the same as the other compilation unit + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext Q_DISABLE_COPY(DeferredData); }; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QVector<DeferredData *> deferredData; - void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *); + void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, QQmlContextData *); void releaseDeferredData(); QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp new file mode 100644 index 0000000000..750fc6de50 --- /dev/null +++ b/src/qml/qml/qqmldatablob.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmldatablob_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmlprofiler_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#include <QtQml/qqmlengine.h> + +#ifdef DATABLOB_DEBUG +#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) +#else +#define ASSERT_CALLBACK() +#endif + +DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); + +QT_BEGIN_NAMESPACE + +/*! +\class QQmlDataBlob +\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader. +\internal + +QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob +and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it. +The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available. +*/ + +/*! +\enum QQmlDataBlob::Status + +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 +*/ + +/*! +\enum QQmlDataBlob::Type + +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 +*/ + +/*! +Create a new QQmlDataBlob for \a url and of the provided \a type. +*/ +QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager) +: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0), + m_inCallback(false), m_isDone(false) +{ + //Set here because we need to get the engine from the manager + if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor()) + m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url, + (QQmlAbstractUrlInterceptor::DataType)m_type); +} + +/*! \internal */ +QQmlDataBlob::~QQmlDataBlob() +{ + Q_ASSERT(m_waitingOnMe.isEmpty()); + + cancelAllWaitingFor(); +} + +/*! + Must be called before loading can occur. +*/ +void QQmlDataBlob::startLoading() +{ + Q_ASSERT(status() == QQmlDataBlob::Null); + m_data.setStatus(QQmlDataBlob::Loading); +} + +/*! +Returns the type provided to the constructor. +*/ +QQmlDataBlob::Type QQmlDataBlob::type() const +{ + return m_type; +} + +/*! +Returns the blob's status. +*/ +QQmlDataBlob::Status QQmlDataBlob::status() const +{ + return m_data.status(); +} + +/*! +Returns true if the status is Null. +*/ +bool QQmlDataBlob::isNull() const +{ + return status() == Null; +} + +/*! +Returns true if the status is Loading. +*/ +bool QQmlDataBlob::isLoading() const +{ + return status() == Loading; +} + +/*! +Returns true if the status is WaitingForDependencies. +*/ +bool QQmlDataBlob::isWaiting() const +{ + return status() == WaitingForDependencies || + status() == ResolvingDependencies; +} + +/*! +Returns true if the status is Complete. +*/ +bool QQmlDataBlob::isComplete() const +{ + return status() == Complete; +} + +/*! +Returns true if the status is Error. +*/ +bool QQmlDataBlob::isError() const +{ + return status() == Error; +} + +/*! +Returns true if the status is Complete or Error. +*/ +bool QQmlDataBlob::isCompleteOrError() const +{ + Status s = status(); + return s == Error || s == Complete; +} + +/*! +Returns the data download progress from 0 to 1. +*/ +qreal QQmlDataBlob::progress() const +{ + quint8 p = m_data.progress(); + if (p == 0xFF) return 1.; + else return qreal(p) / qreal(0xFF); +} + +/*! +Returns the physical url of the data. Initially this is the same as +finalUrl(), but if a URL interceptor is set, it will work on this URL +and leave finalUrl() alone. + +\sa finalUrl() +*/ +QUrl QQmlDataBlob::url() const +{ + return m_url; +} + +QString QQmlDataBlob::urlString() const +{ + if (m_urlString.isEmpty()) + m_urlString = m_url.toString(); + + return m_urlString; +} + +/*! +Returns the logical URL to be used for resolving further URLs referred to in +the code. + +This is the blob url passed to the constructor. If a URL interceptor rewrites +the URL, this one stays the same. If a network redirect happens while fetching +the data, this url is updated to reflect the new location. Therefore, if both +an interception and a redirection happen, the final url will indirectly +incorporate the result of the interception, potentially breaking further +lookups. + +\sa url() +*/ +QUrl QQmlDataBlob::finalUrl() const +{ + return m_finalUrl; +} + +/*! +Returns the finalUrl() as a string. +*/ +QString QQmlDataBlob::finalUrlString() const +{ + if (m_finalUrlString.isEmpty()) + m_finalUrlString = m_finalUrl.toString(); + + return m_finalUrlString; +} + +/*! +Return the errors on this blob. + +May only be called from the load thread, or after the blob isCompleteOrError(). +*/ +QList<QQmlError> QQmlDataBlob::errors() const +{ + Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); + return m_errors; +} + +/*! +Mark this blob as having \a errors. + +All outstanding dependencies will be cancelled. Requests to add new dependencies +will be ignored. Entry into the Error state is irreversable. + +The setError() method may only be called from within a QQmlDataBlob callback. +*/ +void QQmlDataBlob::setError(const QQmlError &errors) +{ + ASSERT_CALLBACK(); + + QList<QQmlError> l; + l << errors; + setError(l); +} + +/*! +\overload +*/ +void QQmlDataBlob::setError(const QList<QQmlError> &errors) +{ + ASSERT_CALLBACK(); + + Q_ASSERT(status() != Error); + Q_ASSERT(m_errors.isEmpty()); + + m_errors = errors; // Must be set before the m_data fence + m_data.setStatus(Error); + + if (dumpErrors()) { + qWarning().nospace() << "Errors for " << urlString(); + for (int ii = 0; ii < errors.count(); ++ii) + qWarning().nospace() << " " << qPrintable(errors.at(ii).toString()); + } + cancelAllWaitingFor(); + + if (!m_inCallback) + tryDone(); +} + +void QQmlDataBlob::setError(const QQmlJS::DiagnosticMessage &error) +{ + QQmlError e; + e.setColumn(error.column); + e.setLine(error.line); + e.setDescription(error.message); + e.setUrl(url()); + setError(e); +} + +void QQmlDataBlob::setError(const QVector<QQmlJS::DiagnosticMessage> &errors) +{ + QList<QQmlError> finalErrors; + finalErrors.reserve(errors.count()); + for (const auto &error : errors) { + QQmlError e; + e.setColumn(error.column); + e.setLine(error.line); + e.setDescription(error.message); + e.setUrl(url()); + finalErrors << e; + } + setError(finalErrors); +} + +void QQmlDataBlob::setError(const QString &description) +{ + QQmlError e; + e.setDescription(description); + e.setUrl(url()); + setError(e); +} + +/*! +Wait for \a blob to become complete or to error. If \a blob is already +complete or in error, or this blob is already complete, this has no effect. + +The setError() method may only be called from within a QQmlDataBlob callback. +*/ +void QQmlDataBlob::addDependency(QQmlDataBlob *blob) +{ + ASSERT_CALLBACK(); + + Q_ASSERT(status() != Null); + + if (!blob || + blob->status() == Error || blob->status() == Complete || + status() == Error || status() == Complete || m_isDone) + return; + + for (const auto &existingDep: qAsConst(m_waitingFor)) + if (existingDep.data() == blob) + return; + + m_data.setStatus(WaitingForDependencies); + + m_waitingFor.append(blob); + blob->m_waitingOnMe.append(this); +} + +/*! +\fn void QQmlDataBlob::dataReceived(const Data &data) + +Invoked when data for the blob is received. Implementors should use this callback +to determine a blob's dependencies. Within this callback you may call setError() +or addDependency(). +*/ + +/*! +Invoked once data has either been received or a network error occurred, and all +dependencies are complete. + +You can set an error in this method, but you cannot add new dependencies. Implementors +should use this callback to finalize processing of data. + +The default implementation does nothing. + +XXX Rename processData() or some such to avoid confusion between done() (processing thread) +and completed() (main thread) +*/ +void QQmlDataBlob::done() +{ +} + +#if QT_CONFIG(qml_network) +/*! +Invoked if there is a network error while fetching this blob. + +The default implementation sets an appropriate QQmlError. +*/ +void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) +{ + Q_UNUSED(networkError); + + QQmlError error; + error.setUrl(m_url); + + const char *errorString = nullptr; + switch (networkError) { + default: + errorString = "Network error"; + break; + case QNetworkReply::ConnectionRefusedError: + errorString = "Connection refused"; + break; + case QNetworkReply::RemoteHostClosedError: + errorString = "Remote host closed the connection"; + break; + case QNetworkReply::HostNotFoundError: + errorString = "Host not found"; + break; + case QNetworkReply::TimeoutError: + errorString = "Timeout"; + break; + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + case QNetworkReply::UnknownProxyError: + errorString = "Proxy error"; + break; + case QNetworkReply::ContentAccessDenied: + errorString = "Access denied"; + break; + case QNetworkReply::ContentNotFoundError: + errorString = "File not found"; + break; + case QNetworkReply::AuthenticationRequiredError: + errorString = "Authentication required"; + break; + }; + + error.setDescription(QLatin1String(errorString)); + + setError(error); +} +#endif // qml_network + +/*! +Called if \a blob, which was previously waited for, has an error. + +The default implementation does nothing. +*/ +void QQmlDataBlob::dependencyError(QQmlDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called if \a blob, which was previously waited for, has completed. + +The default implementation does nothing. +*/ +void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called when all blobs waited for have completed. This occurs regardless of +whether they are in error, or complete state. + +The default implementation does nothing. +*/ +void QQmlDataBlob::allDependenciesDone() +{ + m_data.setStatus(QQmlDataBlob::ResolvingDependencies); +} + +/*! +Called when the download progress of this blob changes. \a progress goes +from 0 to 1. + +This callback is only invoked if an asynchronous load for this blob is +made. An asynchronous load is one in which the Asynchronous mode is +specified explicitly, or one that is implicitly delayed due to a network +operation. + +The default implementation does nothing. +*/ +void QQmlDataBlob::downloadProgressChanged(qreal progress) +{ + Q_UNUSED(progress); +} + +/*! +Invoked on the main thread sometime after done() was called on the load thread. + +You cannot modify the blobs state at all in this callback and cannot depend on the +order or timeliness of these callbacks. Implementors should use this callback to notify +dependencies on the main thread that the blob is done and not a lot else. + +This callback is only invoked if an asynchronous load for this blob is +made. An asynchronous load is one in which the Asynchronous mode is +specified explicitly, or one that is implicitly delayed due to a network +operation. + +The default implementation does nothing. +*/ +void QQmlDataBlob::completed() +{ +} + + +void QQmlDataBlob::tryDone() +{ + if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { + m_isDone = true; + addref(); + +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataBlob::done() %s", qPrintable(urlString())); +#endif + done(); + + if (status() != Error) + m_data.setStatus(Complete); + + notifyAllWaitingOnMe(); + + // Locking is not required here, as anyone expecting callbacks must + // already be protected against the blob being completed (as set above); +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataBlob: Dispatching completed"); +#endif + m_typeLoader->m_thread->callCompleted(this); + + release(); + } +} + +void QQmlDataBlob::cancelAllWaitingFor() +{ + while (m_waitingFor.count()) { + QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast(); + + Q_ASSERT(blob->m_waitingOnMe.contains(this)); + + blob->m_waitingOnMe.removeOne(this); + } +} + +void QQmlDataBlob::notifyAllWaitingOnMe() +{ + while (m_waitingOnMe.count()) { + QQmlDataBlob *blob = m_waitingOnMe.takeLast(); + + Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(), + [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; })); + + blob->notifyComplete(this); + } +} + +void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) +{ + Q_ASSERT(blob->status() == Error || blob->status() == Complete); + QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); + + m_inCallback = true; + + QQmlRefPointer<QQmlDataBlob> blobRef; + for (int i = 0; i < m_waitingFor.count(); ++i) { + if (m_waitingFor.at(i).data() == blob) { + blobRef = m_waitingFor.takeAt(i); + break; + } + } + Q_ASSERT(blobRef); + + if (blob->status() == Error) { + dependencyError(blob); + } else if (blob->status() == Complete) { + dependencyComplete(blob); + } + + if (!isError() && m_waitingFor.isEmpty()) + allDependenciesDone(); + + m_inCallback = false; + + tryDone(); +} + +QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const +{ + error->clear(); + if (hasInlineSourceCode) + return inlineSourceCode; + + QFile f(fileInfo.absoluteFilePath()); + if (!f.open(QIODevice::ReadOnly)) { + *error = f.errorString(); + return QString(); + } + + const qint64 fileSize = fileInfo.size(); + + if (uchar *mappedData = f.map(0, fileSize)) { + QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize); + f.unmap(mappedData); + return source; + } + + QByteArray data(fileSize, Qt::Uninitialized); + if (f.read(data.data(), data.length()) != data.length()) { + *error = f.errorString(); + return QString(); + } + return QString::fromUtf8(data); +} + +QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const +{ + if (hasInlineSourceCode) + return QDateTime(); + + return fileInfo.lastModified(); +} + +bool QQmlDataBlob::SourceCodeData::exists() const +{ + if (hasInlineSourceCode) + return true; + return fileInfo.exists(); +} + +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h new file mode 100644 index 0000000000..0450e94c02 --- /dev/null +++ b/src/qml/qml/qqmldatablob_p.h @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATABLOB_P_H +#define QQMLDATABLOB_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 <private/qqmlrefcount_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qv4compileddata_p.h> + +#if QT_CONFIG(qml_network) +#include <QtNetwork/qnetworkreply.h> +#endif + +#include <QtQml/qqmlerror.h> +#include <QtQml/qqmlabstracturlinterceptor.h> + +#include <QtCore/qdatetime.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeLoader; +class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount +{ +public: + enum Status { + Null, // Prior to QQmlTypeLoader::load() + Loading, // Prior to data being received and dataReceived() being called + WaitingForDependencies, // While there are outstanding addDependency()s + ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles + Complete, // Finished + Error // Error + }; + + enum Type { //Matched in QQmlAbstractUrlInterceptor + QmlFile = QQmlAbstractUrlInterceptor::QmlFile, + JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile, + QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile + }; + + QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager); + ~QQmlDataBlob() override; + + void startLoading(); + + QQmlTypeLoader *typeLoader() const { return m_typeLoader; } + + Type type() const; + + Status status() const; + bool isNull() const; + bool isLoading() const; + bool isWaiting() const; + bool isComplete() const; + bool isError() const; + bool isCompleteOrError() const; + + qreal progress() const; + + QUrl url() const; + QString urlString() const; + QUrl finalUrl() const; + QString finalUrlString() const; + + QList<QQmlError> errors() const; + + class SourceCodeData { + public: + QString readAll(QString *error) const; + QDateTime sourceTimeStamp() const; + bool exists() const; + bool isEmpty() const; + private: + friend class QQmlDataBlob; + friend class QQmlTypeLoader; + QString inlineSourceCode; + QFileInfo fileInfo; + bool hasInlineSourceCode = false; + }; + +protected: + // Can be called from within callbacks + void setError(const QQmlError &); + void setError(const QList<QQmlError> &errors); + void setError(const QQmlJS::DiagnosticMessage &error); + void setError(const QVector<QQmlJS::DiagnosticMessage> &errors); + void setError(const QString &description); + void addDependency(QQmlDataBlob *); + + // Callbacks made in load thread + virtual void dataReceived(const SourceCodeData &) = 0; + virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0; + virtual void done(); +#if QT_CONFIG(qml_network) + virtual void networkError(QNetworkReply::NetworkError); +#endif + virtual void dependencyError(QQmlDataBlob *); + virtual void dependencyComplete(QQmlDataBlob *); + virtual void allDependenciesDone(); + + // Callbacks made in main thread + virtual void downloadProgressChanged(qreal); + virtual void completed(); + +protected: + // Manager that is currently fetching data for me + QQmlTypeLoader *m_typeLoader; + +private: + friend class QQmlTypeLoader; + friend class QQmlTypeLoaderThread; + + void tryDone(); + void cancelAllWaitingFor(); + void notifyAllWaitingOnMe(); + void notifyComplete(QQmlDataBlob *); + + struct ThreadData { + private: + enum { + StatusMask = 0x0000FFFF, + StatusShift = 0, + ProgressMask = 0x00FF0000, + ProgressShift = 16, + AsyncMask = 0x80000000, + NoMask = 0 + }; + + public: + inline ThreadData() + : _p(0) + { + } + + inline QQmlDataBlob::Status status() const + { + return QQmlDataBlob::Status((_p.loadRelaxed() & StatusMask) >> StatusShift); + } + + inline void setStatus(QQmlDataBlob::Status status) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~StatusMask) | ((status << StatusShift) & StatusMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + inline bool isAsync() const + { + return _p.loadRelaxed() & AsyncMask; + } + + inline void setIsAsync(bool v) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~AsyncMask) | (v ? AsyncMask : NoMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + inline quint8 progress() const + { + return quint8((_p.loadRelaxed() & ProgressMask) >> ProgressShift); + } + + inline void setProgress(quint8 v) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~ProgressMask) | ((v << ProgressShift) & ProgressMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + private: + QAtomicInt _p; + }; + ThreadData m_data; + + // m_errors should *always* be written before the status is set to Error. + // We use the status change as a memory fence around m_errors so that locking + // isn't required. Once the status is set to Error (or Complete), m_errors + // cannot be changed. + QList<QQmlError> m_errors; + + Type m_type; + + QUrl m_url; + QUrl m_finalUrl; + mutable QString m_urlString; + mutable QString m_finalUrlString; + + // List of QQmlDataBlob's that are waiting for me to complete. +protected: + QList<QQmlDataBlob *> m_waitingOnMe; +private: + + // List of QQmlDataBlob's that I am waiting for to complete. + QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor; + + int m_redirectCount:30; + bool m_inCallback:1; + bool m_isDone:1; +}; + +QT_END_NAMESPACE + +#endif // QQMLDATABLOB_P_H diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index 61cb0a9065..02fde97b3d 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -38,12 +38,12 @@ ****************************************************************************/ #include "qqmldelayedcallqueue_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qv4value_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <QQmlError> diff --git a/src/qml/qml/qqmldirdata.cpp b/src/qml/qml/qqmldirdata.cpp new file mode 100644 index 0000000000..de74dfdf9b --- /dev/null +++ b/src/qml/qml/qqmldirdata.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmldirdata_p.h> + +QT_BEGIN_NAMESPACE + +QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader) + : QQmlTypeLoader::Blob(url, QmldirFile, loader) +{ +} + +const QString &QQmlQmldirData::content() const +{ + return m_content; +} + +QQmlTypeLoader::Blob::PendingImportPtr QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const +{ + auto it = m_imports.find(blob); + if (it == m_imports.end()) + return nullptr; + return *it; +} + +void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, QQmlTypeLoader::Blob::PendingImportPtr import) +{ + m_imports[blob] = std::move(import); +} + +int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const +{ + QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob); + if (it == m_priorities.end()) + return 0; + return *it; +} + +void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority) +{ + m_priorities[blob] = priority; +} + +void QQmlQmldirData::dataReceived(const SourceCodeData &data) +{ + QString error; + m_content = data.readAll(&error); + if (!error.isEmpty()) { + setError(error); + return; + } +} + +void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) +{ + Q_UNIMPLEMENTED(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldirdata_p.h b/src/qml/qml/qqmldirdata_p.h new file mode 100644 index 0000000000..34f1ff1678 --- /dev/null +++ b/src/qml/qml/qqmldirdata_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDIRDATA_P_H +#define QQMLDIRDATA_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 <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob +{ +private: + friend class QQmlTypeLoader; + + QQmlQmldirData(const QUrl &, QQmlTypeLoader *); + +public: + const QString &content() const; + + PendingImportPtr import(QQmlTypeLoader::Blob *) const; + void setImport(QQmlTypeLoader::Blob *, PendingImportPtr); + + int priority(QQmlTypeLoader::Blob *) const; + void setPriority(QQmlTypeLoader::Blob *, int); + +protected: + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override; + +private: + QString m_content; + QHash<QQmlTypeLoader::Blob *, QQmlTypeLoader::Blob::PendingImportPtr> m_imports; + QHash<QQmlTypeLoader::Blob *, int> m_priorities; +}; + +QT_END_NAMESPACE + +#endif // QQMLDIRDATA_P_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4c6a1b69d8..0fd07ea209 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -51,14 +51,15 @@ #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" -#include "qqmldirparser_p.h" #include "qqmlextensioninterface.h" #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" +#include <private/qqmldirparser_p.h> #include <private/qqmlboundsignal_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <QtCore/qstandardpaths.h> #include <QtCore/qsettings.h> #include <QtCore/qmetaobject.h> @@ -86,19 +87,7 @@ #if QT_CONFIG(qml_animation) #include <private/qqmltimer_p.h> #endif -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#endif #include <private/qqmlplatform_p.h> -#include <private/qquickpackage_p.h> -#if QT_CONFIG(qml_delegate_model) -#include <private/qqmldelegatemodel_p.h> -#endif -#include <private/qqmlobjectmodel_p.h> -#if QT_CONFIG(qml_worker_script) -#include <private/qquickworkerscript_p.h> -#endif -#include <private/qqmlinstantiator_p.h> #include <private/qqmlloggingcategory_p.h> #ifdef Q_OS_WIN // for %APPDATA% @@ -116,13 +105,6 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE -void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) -{ - QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); - QQmlEnginePrivate::registerQtQuick2Types(uri, versionMajor, versionMinor); - QQmlValueTypeFactory::registerValueTypes(uri, versionMajor, versionMinor); -} - // Declared in qqml.h int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, @@ -215,63 +197,56 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, bool QQmlEnginePrivate::qml_debugging_enabled = false; bool QQmlEnginePrivate::s_designerMode = false; -// these types are part of the QML language -void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int versionMinor) +void QQmlEnginePrivate::defineModule() { - qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component"); - qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject"); - qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding"); - qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 - qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser); - if (!strcmp(uri, "QtQuick")) - qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7 - else - qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3 -#if QT_CONFIG(qml_animation) - qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); -#endif - qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 - qmlRegisterType<QQmlInstanceModel>(); + const char uri[] = "QtQml"; - qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8 - qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12 -} + qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component"); + qmlRegisterType<QObject>(uri, 2, 0, "QtObject"); + qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding"); + qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); // Only available in >= 2.8 + qmlRegisterType<QQmlBind, 14>(uri, 2, 14, "Binding"); + // TODO: We won't need Connections to be a custom type anymore once we can drop the + // automatic signal handler inference from undeclared properties. + qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser); + qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3 -// These QtQuick types' implementation resides in the QtQml module -void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) -{ -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility - qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility -#endif -#if QT_CONFIG(qml_worker_script) - qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript"); +#if QT_CONFIG(qml_animation) + qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer"); #endif - qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package"); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, versionMajor, versionMinor, "VisualDataGroup"); + + qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8 + qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12 + +#if QT_CONFIG(qml_locale) + qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); #endif - qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel"); -#endif // < Qt 6 } -void QQmlEnginePrivate::defineQtQuick2Module() +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +void QQmlEnginePrivate::registerQuickTypes() { - // register the base types into the QtQuick namespace - registerBaseTypes("QtQuick",2,0); + // Don't add anything here. These are only for backwards compatibility. - // register the QtQuick2 types which are implemented in the QtQml module. - registerQtQuick2Types("QtQuick",2,0); + const char uri[] = "QtQuick"; + + qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component"); + qmlRegisterType<QObject>(uri, 2, 0, "QtObject"); + qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding"); + qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); + qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser); + qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 7, "Connections", new QQmlConnectionsParser); +#if QT_CONFIG(qml_animation) + qmlRegisterType<QQmlTimer>(uri, 2, 0,"Timer"); +#endif + qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); + qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); #if QT_CONFIG(qml_locale) - qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); #endif - - // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward - qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); } +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) bool QQmlEnginePrivate::designerMode() { @@ -841,19 +816,20 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in // marshalled back onto the QObject's thread and handled by QML from there. This is tested // by the qqmlecmascript::threadSignal() autotest. if (ddata->notifyList && - QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.load()) { + QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.loadRelaxed()) { - if (!QObjectPrivate::get(object)->threadData->thread) + if (!QObjectPrivate::get(object)->threadData->thread.loadAcquire()) return; QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index); QList<QByteArray> parameterTypes = m.parameterTypes(); - int *types = (int *)malloc((parameterTypes.count() + 1) * sizeof(int)); - void **args = (void **) malloc((parameterTypes.count() + 1) *sizeof(void *)); + QScopedPointer<QMetaCallEvent> ev(new QMetaCallEvent(m.methodIndex(), 0, nullptr, + object, index, + parameterTypes.count() + 1)); - types[0] = 0; // return type - args[0] = nullptr; // return value + void **args = ev->args(); + int *types = ev->types(); for (int ii = 0; ii < parameterTypes.count(); ++ii) { const QByteArray &typeName = parameterTypes.at(ii); @@ -866,21 +842,16 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" "(Make sure '%s' is registered using qRegisterMetaType().)", typeName.constData(), typeName.constData()); - free(types); - free(args); return; } args[ii + 1] = QMetaType::create(types[ii + 1], a[ii + 1]); } - QMetaCallEvent *ev = new QMetaCallEvent(m.methodIndex(), 0, nullptr, object, index, - parameterTypes.count() + 1, types, args); - QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject; mpo->target = object; - mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread); - QCoreApplication::postEvent(mpo, ev); + mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread.loadAcquire()); + QCoreApplication::postEvent(mpo, ev.take()); } else { QQmlNotifierEndpoint *ep = ddata->notify(index); @@ -975,14 +946,10 @@ void QQmlEnginePrivate::init() Q_Q(QQmlEngine); if (baseModulesUninitialized) { - qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler. - registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. -#if QT_CONFIG(qml_locale) - qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); -#endif - // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward - qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); + // required for the Compiler. + qmlRegisterType<QObject>("QML", 1, 0, "QtObject"); + qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); QQmlData::init(); baseModulesUninitialized = false; @@ -994,24 +961,13 @@ void QQmlEnginePrivate::init() qRegisterMetaType<QQmlComponent::Status>(); qRegisterMetaType<QList<QObject*> >(); qRegisterMetaType<QList<int> >(); - qRegisterMetaType<QQmlV4Handle>(); qRegisterMetaType<QQmlBinding*>(); - v8engine()->setEngine(q); + q->handle()->setQmlEngine(q); rootContext = new QQmlContext(q,true); } -#if QT_CONFIG(qml_worker_script) -QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() -{ - Q_Q(QQmlEngine); - if (!workerScriptEngine) - workerScriptEngine = new QQuickWorkerScriptEngine(q); - return workerScriptEngine; -} -#endif - /*! \class QQmlEngine \since 5.0 @@ -1091,7 +1047,7 @@ QQmlEngine::~QQmlEngine() // XXX TODO: performance -- store list of singleton types separately? QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes(); for (const QQmlType &currType : singletonTypes) - currType.singletonInstanceInfo()->destroy(this); + d->destroySingletonInstance(currType); delete d->rootContext; d->rootContext = nullptr; @@ -1214,6 +1170,13 @@ void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) } } +QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const +{ + const QString providerIdLower = providerId.toLower(); + QMutexLocker locker(&mutex); + return imageProviders.value(providerIdLower); +} + #if QT_CONFIG(qml_network) /*! Sets the \a factory to use for creating QNetworkAccessManager(s). @@ -1303,8 +1266,10 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider) { Q_D(QQmlEngine); + QString providerIdLower = providerId.toLower(); + QSharedPointer<QQmlImageProviderBase> sp(provider); QMutexLocker locker(&d->mutex); - d->imageProviders.insert(providerId.toLower(), QSharedPointer<QQmlImageProviderBase>(provider)); + d->imageProviders.insert(std::move(providerIdLower), std::move(sp)); } /*! @@ -1315,8 +1280,9 @@ void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBa QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const { Q_D(const QQmlEngine); + const QString providerIdLower = providerId.toLower(); QMutexLocker locker(&d->mutex); - return d->imageProviders.value(providerId.toLower()).data(); + return d->imageProviders.value(providerIdLower).data(); } /*! @@ -1327,8 +1293,9 @@ QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) cons void QQmlEngine::removeImageProvider(const QString &providerId) { Q_D(QQmlEngine); + const QString providerIdLower = providerId.toLower(); QMutexLocker locker(&d->mutex); - d->imageProviders.take(providerId.toLower()); + d->imageProviders.take(providerIdLower); } /*! @@ -1436,23 +1403,13 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) template<> QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) { + Q_D(QQmlEngine); QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType); if (!type.isValid() || !type.isSingleton()) return QJSValue(); - QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo(); - info->init(this); - - if (QObject* o = info->qobjectApi(this)) - return this->newQObject(o); - else { - QJSValue value = info->scriptApi(this); - if (!value.isUndefined()) - return value; - } - - return QJSValue(); + return d->singletonInstance<QJSValue>(type); } /*! @@ -1669,6 +1626,7 @@ static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlDat return rv; } +#if QT_DEPRECATED_SINCE(5, 14) QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) { QQmlData *data = QQmlData::get(object, create); @@ -1696,6 +1654,7 @@ QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, return qmlAttachedPropertiesObjectById(*idCache, object, create); } +#endif QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object, const QMetaObject *attachedMetaObject) @@ -1824,7 +1783,7 @@ void QQmlData::NotifyList::layout() todo = nullptr; } -void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context) +void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *context) { QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = objectIndex; @@ -1832,7 +1791,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData deferData->context = context; const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex); - const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex); + const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex); const QV4::CompiledData::Binding *binding = compiledObject->bindingTable(); for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) { @@ -2157,20 +2116,21 @@ void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError dumpwarning(error); } -QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(const QString &fileName, const QList<DiagnosticMessage> &diagnosticMessages) +QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics( + const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages) { QList<QQmlError> errors; - for (const DiagnosticMessage &m : diagnosticMessages) { + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message)); continue; } QQmlError error; error.setUrl(QUrl(fileName)); error.setDescription(m.message); - error.setLine(m.loc.startLine); - error.setColumn(m.loc.startColumn); + error.setLine(m.line); + error.setColumn(m.column); errors << error; } return errors; @@ -2298,6 +2258,7 @@ void QQmlEngine::setPluginPathList(const QStringList &paths) d->importDatabase.setPluginPathList(paths); } +#if QT_CONFIG(library) /*! Imports the plugin named \a filePath with the \a uri provided. Returns true if the plugin was successfully imported; otherwise returns false. @@ -2311,6 +2272,7 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList Q_D(QQmlEngine); return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), -1, errors); } +#endif /*! \property QQmlEngine::offlineStoragePath @@ -2365,6 +2327,17 @@ QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex()); } +// #### Qt 6: Remove this function, it exists only for binary compatibility. +/*! + * \internal + */ +bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName) +{ + Q_UNUSED(name) + Q_UNUSED(fileName) + return false; +} + QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const { Q_Q(const QQmlEngine); @@ -2461,7 +2434,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe } } -void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { compilationUnit->isRegisteredWithEngine = true; @@ -2471,7 +2444,7 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::Compila m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit); } -void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { compilationUnit->isRegisteredWithEngine = false; @@ -2479,6 +2452,70 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::Compi m_compositeTypes.remove(compilationUnit->metaTypeId); } +template<> +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(); + Q_ASSERT(siinfo != nullptr); + + if (siinfo->scriptCallback) { + value = siinfo->scriptCallback(q, q); + if (value.isQObject()) { + QObject *o = value.toQObject(); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + } + singletonInstances.insert(type, value); + + } else if (siinfo->qobjectCallback) { + QObject *o = siinfo->qobjectCallback(q, q); + if (!o) { + QQmlError error; + error.setMessageType(QtMsgType::QtCriticalMsg); + error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", + qPrintable(QString::fromUtf8(type.typeName())))); + warning(error); + } else { + // if this object can use a property cache, create it now + QQmlData::ensurePropertyCache(q, o); + } + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + value = q->newQObject(o); + singletonInstances.insert(type, value); + } else if (!siinfo->url.isEmpty()) { + QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous); + QObject *o = component.beginCreate(q->rootContext()); + value = q->newQObject(o); + singletonInstances.insert(type, value); + component.completeCreate(); + } + + return value; +} + +void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type) +{ + Q_ASSERT(type.isSingleton() || type.isCompositeSingleton()); + + QObject* o = singletonInstances.take(type).toQObject(); + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (type.singletonInstanceInfo()->url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) + return; + delete o; + } +} + bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const { return typeLoader.isTypeLoaded(url); diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 871e6bd9b4..31fe3a1849 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -114,7 +114,9 @@ public: bool addNamedBundle(const QString &name, const QString &fileName); +#if QT_CONFIG(library) bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); +#endif #if QT_CONFIG(qml_network) void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *); @@ -175,12 +177,7 @@ Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId); template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) { - QJSValue instance = singletonInstance<QJSValue>(qmlTypeId); - if (!instance.isQObject()) - return nullptr; - - QObject *object = instance.toQObject(); - return qobject_cast<T>(object); + return qobject_cast<T>(singletonInstance<QJSValue>(qmlTypeId).toQObject()); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 92e56cd957..385ae02ce5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -62,11 +62,11 @@ #include "qqmlcontext_p.h" #include "qqmlexpression.h" #include "qqmlproperty_p.h" -#include "qqmlpropertycache_p.h" #include "qqmlmetatype_p.h" #include <private/qintrusivelist_p.h> #include <private/qrecyclepool_p.h> #include <private/qfieldlist_p.h> +#include <private/qv4engine_p.h> #include <QtCore/qlist.h> #include <QtCore/qpair.h> @@ -77,7 +77,6 @@ #include <private/qobject_p.h> -#include <private/qv8engine_p.h> #include <private/qjsengine_p.h> #include <private/qqmldirparser_p.h> @@ -95,12 +94,12 @@ class QQmlTypeNameCache; class QQmlComponentAttached; class QQmlCleanup; class QQmlDelayedError; -class QQuickWorkerScriptEngine; class QQmlObjectCreator; class QDir; class QQmlIncubator; class QQmlProfiler; class QQmlPropertyCapture; +class QQmlMetaObject; // 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 @@ -150,12 +149,10 @@ public: QQmlDelayedError *erroredBindings; int inProgressCreations; - QV8Engine *v8engine() const { return q_func()->handle()->v8Engine; } QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); } #if QT_CONFIG(qml_worker_script) - QQuickWorkerScriptEngine *getWorkerScriptEngine(); - QQuickWorkerScriptEngine *workerScriptEngine; + QThread *workerScriptEngine; #endif QUrl baseUrl; @@ -171,6 +168,8 @@ public: mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory; #endif QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders; + QSharedPointer<QQmlImageProviderBase> imageProvider(const QString &providerId) const; + QQmlAbstractUrlInterceptor* urlInterceptor; @@ -223,12 +222,16 @@ public: QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1); - void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); - void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); + void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; + template <typename T> + T singletonInstance(const QQmlType &type); + void destroySingletonInstance(const QQmlType &type); + void sendQuit(); void sendExit(int retCode = 0); void warning(const QQmlError &); @@ -238,7 +241,6 @@ public: static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList<QQmlError> &); - inline static QV8Engine *getV8Engine(QQmlEngine *e); inline static QV4::ExecutionEngine *getV4Engine(QQmlEngine *e); inline static QQmlEnginePrivate *get(QQmlEngine *e); inline static const QQmlEnginePrivate *get(const QQmlEngine *e); @@ -249,9 +251,10 @@ public: static QList<QQmlError> qmlErrorFromDiagnostics(const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages); - static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); - static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor); - static void defineQtQuick2Module(); + static void defineModule(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + static void registerQuickTypes(); +#endif static bool designerMode(); static void activateDesignerMode(); @@ -261,9 +264,11 @@ public: mutable QMutex networkAccessManagerMutex; private: + QHash<QQmlType, QJSValue> singletonInstances; + // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. - QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes; + QHash<int, QV4::ExecutableCompilationUnit *> m_compositeTypes; static bool s_designerMode; // These members is protected by the full QQmlEnginePrivate::mutex mutex @@ -381,13 +386,6 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(const QQmlType &type, int minorVersi return QQmlMetaType::propertyCache(type, minorVersion); } -QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e) -{ - Q_ASSERT(e); - - return e->handle()->v8Engine; -} - QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e) { Q_ASSERT(e); @@ -434,6 +432,14 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) return get(qmlEngine); } +template<> +Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type); + +template<typename T> +T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { + return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject()); +} + QT_END_NAMESPACE #endif // QQMLENGINE_P_H diff --git a/src/qml/qml/qqmlenumdata_p.h b/src/qml/qml/qqmlenumdata_p.h new file mode 100644 index 0000000000..df99c2c1bc --- /dev/null +++ b/src/qml/qml/qqmlenumdata_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENUMDATA_P_H +#define QQMLENUMDATA_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 <private/qqmlenumvalue_p.h> + +QT_BEGIN_NAMESPACE + +struct QQmlEnumData +{ + QString name; + QVector<QQmlEnumValue> values; +}; + +QT_END_NAMESPACE + +#endif // QQMLENUMDATA_P_H diff --git a/src/qml/qml/qqmlenumvalue_p.h b/src/qml/qml/qqmlenumvalue_p.h new file mode 100644 index 0000000000..ea0fc244cb --- /dev/null +++ b/src/qml/qml/qqmlenumvalue_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENUMVALUE_P_H +#define QQMLENUMVALUE_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/qstring.h> + +QT_BEGIN_NAMESPACE + +struct QQmlEnumValue +{ + QQmlEnumValue() {} + QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {} + QString namedValue; + int value = -1; +}; + +QT_END_NAMESPACE + +#endif // QQMLENUMVALUE_P_H diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp new file mode 100644 index 0000000000..0b94ed3b49 --- /dev/null +++ b/src/qml/qml/qqmlerror.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlerror.h" +#include "qqmlfile.h" +#include "qqmlsourcecoordinate_p.h" +#include <private/qqmljsdiagnosticmessage_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> + +#include <QtCore/qobject.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlError + \since 5.0 + \inmodule QtQml + \brief The QQmlError class encapsulates a QML error. + + QQmlError includes a textual description of the error, as well + as location information (the file, line, and column). The toString() + method creates a single-line, human-readable string containing all of + this information, for example: + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + \endcode + + You can use qDebug(), qInfo(), or qWarning() to output errors to the console. + This method will attempt to open the file indicated by the error + and include additional contextual information. + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + y: "hello" + ^ + \endcode + + \sa QQuickView::errors(), QQmlComponent::errors() +*/ +class QQmlErrorPrivate : public QQmlJS::DiagnosticMessage +{ +public: + QQmlErrorPrivate() { type = QtWarningMsg; } + QUrl url; + QPointer<QObject> object; +}; + +/*! + Creates an empty error object. +*/ +QQmlError::QQmlError() +: d(nullptr) +{ +} + +/*! + Creates a copy of \a other. +*/ +QQmlError::QQmlError(const QQmlError &other) +: d(nullptr) +{ + *this = other; +} + +/*! + Assigns \a other to this error object. +*/ +QQmlError &QQmlError::operator=(const QQmlError &other) +{ + if (!other.d) { + delete d; + d = nullptr; + } else { + if (!d) + d = new QQmlErrorPrivate; + d->url = other.d->url; + d->message = other.d->message; + d->line = other.d->line; + d->column = other.d->column; + d->object = other.d->object; + d->type = other.d->type; + } + return *this; +} + +/*! + \internal +*/ +QQmlError::~QQmlError() +{ + delete d; d = nullptr; +} + +/*! + Returns true if this error is valid, otherwise false. +*/ +bool QQmlError::isValid() const +{ + return d != nullptr; +} + +/*! + Returns the url for the file that caused this error. +*/ +QUrl QQmlError::url() const +{ + if (d) + return d->url; + return QUrl(); +} + +/*! + Sets the \a url for the file that caused this error. +*/ +void QQmlError::setUrl(const QUrl &url) +{ + if (!d) + d = new QQmlErrorPrivate; + d->url = url; +} + +/*! + Returns the error description. +*/ +QString QQmlError::description() const +{ + if (d) + return d->message; + return QString(); +} + +/*! + Sets the error \a description. +*/ +void QQmlError::setDescription(const QString &description) +{ + if (!d) + d = new QQmlErrorPrivate; + d->message = description; +} + +/*! + Returns the error line number. +*/ +int QQmlError::line() const +{ + if (d) + return qmlConvertSourceCoordinate<quint32, int>(d->line); + return -1; +} + +/*! + Sets the error \a line number. +*/ +void QQmlError::setLine(int line) +{ + if (!d) + d = new QQmlErrorPrivate; + d->line = qmlConvertSourceCoordinate<int, quint32>(line); +} + +/*! + Returns the error column number. +*/ +int QQmlError::column() const +{ + if (d) + return qmlConvertSourceCoordinate<quint32, int>(d->column); + return -1; +} + +/*! + Sets the error \a column number. +*/ +void QQmlError::setColumn(int column) +{ + if (!d) + d = new QQmlErrorPrivate; + d->column = qmlConvertSourceCoordinate<int, quint32>(column); +} + +/*! + Returns the nearest object where this error occurred. + Exceptions in bound property expressions set this to the object + to which the property belongs. It will be 0 for all + other exceptions. + */ +QObject *QQmlError::object() const +{ + if (d) + return d->object; + return nullptr; +} + +/*! + Sets the nearest \a object where this error occurred. + */ +void QQmlError::setObject(QObject *object) +{ + if (!d) + d = new QQmlErrorPrivate; + d->object = object; +} + +/*! + \since 5.9 + + Returns the message type. + */ +QtMsgType QQmlError::messageType() const +{ + if (d) + return d->type; + return QtMsgType::QtWarningMsg; +} + +/*! + \since 5.9 + + Sets the \a messageType for this message. The message type determines which + QDebug handlers are responsible for receiving the message. + */ +void QQmlError::setMessageType(QtMsgType messageType) +{ + if (!d) + d = new QQmlErrorPrivate; + d->type = messageType; +} + +/*! + Returns the error as a human readable string. +*/ +QString QQmlError::toString() const +{ + QString rv; + + QUrl u(url()); + int l(line()); + + if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty())) + rv += QLatin1String("<Unknown File>"); + else + rv += u.toString(); + + if (l != -1) { + rv += QLatin1Char(':') + QString::number(l); + + int c(column()); + if (c != -1) + rv += QLatin1Char(':') + QString::number(c); + } + + rv += QLatin1String(": ") + description(); + + return rv; +} + +/*! + \relates QQmlError + \fn QDebug operator<<(QDebug debug, const QQmlError &error) + + Outputs a human readable version of \a error to \a debug. +*/ + +QDebug operator<<(QDebug debug, const QQmlError &error) +{ + debug << qPrintable(error.toString()); + + QUrl url = error.url(); + + if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) { + QString file = QQmlFile::urlToLocalFileOrQrc(url); + QFile f(file); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QTextStream stream(data, QIODevice::ReadOnly); +#if QT_CONFIG(textcodec) + stream.setCodec("UTF-8"); +#endif + const QString code = stream.readAll(); + const auto lines = code.splitRef(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QStringRef &line = lines.at(error.line() - 1); + debug << "\n " << line.toLocal8Bit().constData(); + + if(error.column() > 0) { + int column = qMax(0, error.column() - 1); + column = qMin(column, line.length()); + + QByteArray ind; + ind.reserve(column); + for (int i = 0; i < column; ++i) { + const QChar ch = line.at(i); + if (ch.isSpace()) + ind.append(ch.unicode()); + else + ind.append(' '); + } + ind.append('^'); + debug << "\n " << ind.constData(); + } + } + } + } + return debug; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlerror.h b/src/qml/qml/qqmlerror.h new file mode 100644 index 0000000000..8ea493c11d --- /dev/null +++ b/src/qml/qml/qqmlerror.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLERROR_H +#define QQMLERROR_H + +#include <QtQml/qtqmlglobal.h> + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +// ### Qt 6: should this be called QQmlMessage, since it can have a message type? +class QDebug; +class QQmlErrorPrivate; +class Q_QML_EXPORT QQmlError +{ +public: + QQmlError(); + QQmlError(const QQmlError &); + QQmlError &operator=(const QQmlError &); + ~QQmlError(); + + bool isValid() const; + + QUrl url() const; + void setUrl(const QUrl &); + QString description() const; + void setDescription(const QString &); + int line() const; + void setLine(int); + int column() const; + void setColumn(int); + + QObject *object() const; + void setObject(QObject *); + + QtMsgType messageType() const; + void setMessageType(QtMsgType messageType); + + QString toString() const; +private: + QQmlErrorPrivate *d; +}; + +QDebug Q_QML_EXPORT operator<<(QDebug debug, const QQmlError &error); + +Q_DECLARE_TYPEINFO(QQmlError, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QQMLERROR_H diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index ac2629979f..f6a5afb891 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -45,8 +45,8 @@ #include "qqmlcontext_p.h" #include "qqmlscriptstring_p.h" #include "qqmlbinding_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlsourcecoordinate_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> @@ -352,7 +352,7 @@ QString QQmlExpression::sourceFile() const int QQmlExpression::lineNumber() const { Q_D(const QQmlExpression); - return qmlSourceCoordinate(d->line); + return qmlConvertSourceCoordinate<quint16, int>(d->line); } /*! @@ -362,7 +362,7 @@ int QQmlExpression::lineNumber() const int QQmlExpression::columnNumber() const { Q_D(const QQmlExpression); - return qmlSourceCoordinate(d->column); + return qmlConvertSourceCoordinate<quint16, int>(d->column); } /*! @@ -373,8 +373,8 @@ void QQmlExpression::setSourceLocation(const QString &url, int line, int column) { Q_D(QQmlExpression); d->url = url; - d->line = qmlSourceCoordinate(line); - d->column = qmlSourceCoordinate(column); + d->line = qmlConvertSourceCoordinate<int, quint16>(line); + d->column = qmlConvertSourceCoordinate<int, quint16>(column); } /*! diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 1d60c518c4..acebb6bac3 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -149,7 +149,8 @@ QVariant QQmlValueTypeProvider::createVariantFromString(int type, const QString return QVariant(); } -QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, QQmlV4Handle obj, QV4::ExecutionEngine *e, bool *ok) +QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, const QV4::Value &obj, + QV4::ExecutionEngine *e, bool *ok) { QVariant v; @@ -225,63 +226,53 @@ bool QQmlValueTypeProvider::createFromString(int, const QString &, void *, size_ bool QQmlValueTypeProvider::createStringFrom(int, const void *, QString *) { return false; } bool QQmlValueTypeProvider::variantFromString(const QString &, QVariant *) { return false; } bool QQmlValueTypeProvider::variantFromString(int, const QString &, QVariant *) { return false; } -bool QQmlValueTypeProvider::variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *) { return false; } +bool QQmlValueTypeProvider::variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *) { return false; } bool QQmlValueTypeProvider::equal(int, const void *, const QVariant&) { return false; } bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return false; } bool QQmlValueTypeProvider::read(const QVariant&, void *, int) { return false; } bool QQmlValueTypeProvider::write(int, const void *, QVariant&) { return false; } -Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider) -static QQmlValueTypeProvider *valueTypeProvider = nullptr; +struct ValueTypeProviderList { + QQmlValueTypeProvider nullProvider; + QQmlValueTypeProvider *head = &nullProvider; +}; -static QQmlValueTypeProvider **getValueTypeProvider(void) -{ - if (valueTypeProvider == nullptr) { - valueTypeProvider = nullValueTypeProvider; - } - - return &valueTypeProvider; -} +Q_GLOBAL_STATIC(ValueTypeProviderList, valueTypeProviders) Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newProvider) { - static QQmlValueTypeProvider **providerPtr = getValueTypeProvider(); - newProvider->next = *providerPtr; - *providerPtr = newProvider; + if (ValueTypeProviderList *providers = valueTypeProviders()) { + newProvider->next = providers->head; + providers->head = newProvider; + } } Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider) { - if (oldProvider == nullValueTypeProvider) { - // don't remove the null provider - // we get here when the QtQml library is being unloaded - return; - } - - // the only entry with next = 0 is the null provider - Q_ASSERT(oldProvider->next); + if (ValueTypeProviderList *providers = valueTypeProviders()) { + QQmlValueTypeProvider *prev = providers->head; + if (prev == oldProvider) { + providers->head = oldProvider->next; + return; + } - QQmlValueTypeProvider *prev = valueTypeProvider; - if (prev == oldProvider) { - valueTypeProvider = oldProvider->next; - return; - } + // singly-linked list removal + for (; prev; prev = prev->next) { + if (prev->next != oldProvider) + continue; // this is not the provider you're looking for + prev->next = oldProvider->next; + return; + } - // singly-linked list removal - for ( ; prev; prev = prev->next) { - if (prev->next != oldProvider) - continue; // this is not the provider you're looking for - prev->next = oldProvider->next; - return; + qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider); } - - qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider); } -Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void) +Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider() { - static QQmlValueTypeProvider **providerPtr = getValueTypeProvider(); - return *providerPtr; + if (ValueTypeProviderList *providers = valueTypeProviders()) + return providers->head; + return nullptr; } QQmlColorProvider::~QQmlColorProvider() {} diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 818537560c..3c540a6124 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -53,9 +53,8 @@ #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlmetaobject_p.h> #include <private/qmetaobject_p.h> -#include <private/qv8engine_p.h> QT_BEGIN_NAMESPACE @@ -91,7 +90,7 @@ QT_BEGIN_NAMESPACE \endcode */ #define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \ -{ \ +do { \ SenderType *sender = (Sender); \ ReceiverType *receiver = (Receiver); \ const char *signal = (Signal); \ @@ -99,11 +98,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -112,7 +111,7 @@ QT_BEGIN_NAMESPACE } \ Q_ASSERT(signalIdx != -1 && methodIdx != -1); \ QMetaObject::connect(sender, signalIdx, receiver, methodIdx, Qt::DirectConnection); \ -} +} while (0) /*! Disconnect \a Signal of \a Sender from \a Method of \a Receiver. \a Signal must be @@ -130,7 +129,7 @@ QT_BEGIN_NAMESPACE \endcode */ #define qmlobject_disconnect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \ -{ \ +do { \ SenderType *sender = (Sender); \ ReceiverType *receiver = (Receiver); \ const char *signal = (Signal); \ @@ -138,11 +137,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -151,7 +150,7 @@ QT_BEGIN_NAMESPACE } \ Q_ASSERT(signalIdx != -1 && methodIdx != -1); \ QMetaObject::disconnect(sender, signalIdx, receiver, methodIdx); \ -} +} while (0) /*! This method is identical to qobject_cast<T>() except that it does not require lazy @@ -233,7 +232,7 @@ public: QVariant createVariantFromString(const QString &); QVariant createVariantFromString(int, const QString &, bool *); - QVariant createVariantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, bool*); + QVariant createVariantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, bool *); bool equalValueType(int, const void *, const QVariant&); bool storeValueType(int, const void *, void *, size_t); @@ -250,7 +249,7 @@ private: virtual bool variantFromString(const QString &, QVariant *); virtual bool variantFromString(int, const QString &, QVariant *); - virtual bool variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *); + virtual bool variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *); virtual bool equal(int, const void *, const QVariant&); virtual bool store(int, const void *, void *, size_t); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ba8dce4b6e..3bf8d807a9 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -54,6 +54,8 @@ #include <private/qqmltypenamecache_p.h> #include <private/qqmlengine_p.h> #include <private/qfieldlist_p.h> +#include <private/qqmltypemodule_p.h> +#include <private/qqmltypeloaderqmldircontent_p.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonarray.h> @@ -131,85 +133,6 @@ bool isPathAbsolute(const QString &path) #endif } -/* - \internal - - Fetches the QQmlType instance registered for \a urlString, creating a - registration for it if it is not already registered, using the associated - \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion - details. - - Errors (if there are any) are placed into \a errors, if it is nonzero. Note - that errors are treated as fatal if \a errors is not set. -*/ -QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, - bool isCompositeSingleton, QList<QQmlError> *errors, - int majorVersion=-1, int minorVersion=-1) -{ - QUrl url(urlString); // ### unfortunate (costly) conversion - QQmlType ret = QQmlMetaType::qmlType(url); - if (ret.isValid()) - return ret; - - int dot = typeName.indexOf(QLatin1Char('.')); - QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); - - // We need a pointer, but we were passed a string. Take a copy so we - // can guarentee it will live long enough to reach qmlregister. - QByteArray buf(unqualifiedtype.toString().toUtf8()); - - QQmlMetaTypeRegistrationFailureRecorder failureRecorder; - - // 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. - if (isCompositeSingleton) { - QQmlPrivate::RegisterCompositeSingletonType reg = { - url, - "", // uri - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::registerCompositeSingletonType(reg); - } else { - QQmlPrivate::RegisterCompositeType reg = { - url, - "", // uri - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::registerCompositeType(reg); - } - - // 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 (!ret.isValid()) { - if (!errors) // Cannot list errors properly, just quit - qFatal("%s", failureRecorder.failures().join('\n').toLatin1().constData()); - QQmlError error; - error.setDescription(failureRecorder.failures().join('\n')); - errors->prepend(error); - } - return ret; -} - } // namespace struct RegisteredPlugin { @@ -219,6 +142,13 @@ struct RegisteredPlugin { struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> { QMutex mutex; + + ~StringRegisteredPluginMap() + { + QMutexLocker lock(&mutex); + for (const RegisteredPlugin &plugin : qAsConst(*this)) + delete plugin.loader; + } }; Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders @@ -831,9 +761,9 @@ 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 = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, - nullptr, candidate->majorVersion, - candidate->minorVersion); + QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, isCompositeSingleton, + nullptr, candidate->majorVersion, + candidate->minorVersion); if (vmajor) *vmajor = candidate->majorVersion; if (vminor) @@ -850,12 +780,12 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt typeStr + dotqml_string, // Type -> Type.qml typeStr + dotuidotqml_string // Type -> Type.ui.qml }; - for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) { - exists = typeLoader->fileExists(localDirectoryPath, urlsToTry[i]); + for (const QString &urlToTry : urlsToTry) { + exists = typeLoader->fileExists(localDirectoryPath, urlToTry); if (exists) { #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) // don't let function.qml confuse the use of "new Function(...)" for example. - if (!QQml_isFileCaseCorrect(localDirectoryPath + urlsToTry[i])) { + if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) { exists = false; if (errors) { QQmlError caseError; @@ -867,7 +797,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt #else Q_UNUSED(errors); #endif - qmlUrl = url + urlsToTry[i]; + qmlUrl = url + urlToTry; break; } } @@ -877,8 +807,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType returnType = fetchOrCreateTypeForUrl( - qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors); + QQmlType returnType = QQmlMetaType::typeForUrl( + qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors); if (type_return) *type_return = returnType; return returnType.isValid(); @@ -926,7 +856,10 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); + *type_return = QQmlMetaType::typeForUrl( + resolveLocalUrl(s->imports.at(0)->url, + unqualifiedtype.toString() + QLatin1String(".qml")), + type, false, errors); return type_return->isValid(); } } @@ -1099,13 +1032,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res return true; } -#if defined(QT_SHARED) || !QT_CONFIG(library) -static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) -{ - return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why); -} -#endif - /* Import an extension defined by a qmldir file. @@ -1154,7 +1080,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, int dynamicPluginsFound = 0; int staticPluginsFound = 0; -#if defined(QT_SHARED) +#if QT_CONFIG(library) const auto qmldirPlugins = qmldir.plugins(); for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) { QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); @@ -1165,9 +1091,11 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, // XXX TODO: should we leave the import plugin error alone? // Here, we pop it off the top and coalesce it into this error's message. // The reason is that the lower level may add url and line/column numbering information. - QQmlError poppedError = errors->takeFirst(); QQmlError error; - error.setDescription(msgCannotLoadPlugin(uri, poppedError.description())); + error.setDescription( + QQmlImportDatabase::tr( + "plugin cannot be loaded for module \"%1\": %2") + .arg(uri, errors->takeFirst().description())); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } @@ -1175,7 +1103,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } } } -#endif // QT_SHARED +#endif // QT_CONFIG(library) if (dynamicPluginsFound < qmldirPluginCount) { // Check if the missing plugins can be resolved statically. We do this by looking at @@ -1823,6 +1751,16 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e) addImportPath(QStringLiteral("qrc:/qt-project.org/imports")); addImportPath(QCoreApplication::applicationDirPath()); +#if defined(Q_OS_ANDROID) + addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml")); + if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) { + const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH"); + QLatin1Char pathSep(':'); + QStringList paths = envImportPath.split(pathSep, QString::SkipEmptyParts); + for (int ii = paths.count() - 1; ii >= 0; --ii) + addPluginPath(paths.at(ii)); + } +#endif } QQmlImportDatabase::~QQmlImportDatabase() @@ -1870,6 +1808,19 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, if (!resolvedPath.endsWith(Slash)) resolvedPath += Slash; +#if defined(Q_OS_ANDROID) + if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':') && qmldirPath.at(1) == QLatin1Char('/') && + qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), Qt::CaseInsensitive)) { + QString pluginName = qmldirPath.mid(21) + Slash + baseName; + pluginName.replace(QLatin1Char('/'), QLatin1Char('_')); + QString bundledPath = resolvedPath + QLatin1String("lib") + pluginName; + for (const QString &suffix : suffixes) { + const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix); + if (!absolutePath.isEmpty()) + return absolutePath; + } + } +#endif resolvedPath += prefix + baseName; for (const QString &suffix : suffixes) { const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); @@ -1924,9 +1875,15 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, QLatin1String(".so"), QLatin1String(".bundle") }; -# else // Unix +#else // Unix static const QString prefix = QLatin1String("lib"); - static const QStringList suffixes = { QLatin1String(".so") }; + static const QStringList suffixes = { +# if defined(Q_OS_ANDROID) + QStringLiteral(LIBS_SUFFIX), +# endif + QLatin1String(".so") + + }; #endif return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, suffixes, prefix); @@ -2030,7 +1987,9 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths) if (qmlImportTrace()) qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths; - fileImportPath = paths; + fileImportPath.clear(); + for (auto it = paths.crbegin(); it != paths.crend(); ++it) + addImportPath(*it); // Our existing cached paths may have been invalidated clearDirCache(); @@ -2044,77 +2003,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b { if (qmlImportTrace()) qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath; - - QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance); - if (!iface) { - if (errors) { - QQmlError error; - error.setDescription(tr("Module loaded for URI '%1' does not implement QQmlTypesExtensionInterface").arg(typeNamespace)); - errors->prepend(error); - } - return false; - } - - const QByteArray bytes = uri.toUtf8(); - const char *moduleId = bytes.constData(); - - QQmlMetaTypeRegistrationFailureRecorder failureRecorder; - { - // Create a scope for QWriteLocker to keep it as narrow as possible, and - // to ensure that we release it before the call to initalizeEngine below - QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); - - if (!typeNamespace.isEmpty()) { - // This is an 'identified' module - if (typeNamespace != uri) { - // The namespace for type registrations must match the URI for locating the module - if (errors) { - QQmlError error; - error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri)); - errors->prepend(error); - } - return false; - } - - if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace, vmaj)) { - // Other modules have already installed to this namespace - if (errors) { - QQmlError error; - error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace)); - errors->prepend(error); - } - return false; - } else { - QQmlMetaType::protectNamespace(typeNamespace); - } - } else { - // This is not an identified module - provide a warning - qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module identifier directive - it cannot be protected from external registrations.").arg(uri)); - } - - QQmlMetaType::setTypeRegistrationNamespace(typeNamespace); - - if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { - // basepath should point to the directory of the module, not the plugin file itself: - QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); - } - - iface->registerTypes(moduleId); - QQmlMetaType::setTypeRegistrationNamespace(QString()); - } // QWriteLocker lock(QQmlMetaType::typeRegistrationLock()) - - if (!failureRecorder.failures().isEmpty()) { - if (errors) { - for (const QString &failure : failureRecorder.failures()) { - QQmlError error; - error.setDescription(failure); - errors->prepend(error); - } - } - return false; - } - - return true; + return QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors); } /*! @@ -2169,13 +2058,13 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba return true; } +#if QT_CONFIG(library) /*! \internal */ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors) { -#if QT_CONFIG(library) QFileInfo fileInfo(filePath); const QString absoluteFilePath = fileInfo.absoluteFilePath(); @@ -2253,20 +2142,14 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr } return true; -#else - Q_UNUSED(filePath); - Q_UNUSED(uri); - Q_UNUSED(typeNamespace); - Q_UNUSED(vmaj); - Q_UNUSED(errors); - return false; -#endif } +#endif // QT_CONFIG(library) + void QQmlImportDatabase::clearDirCache() { - QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.begin(); - while (itr != qmldirCache.end()) { + QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.constBegin(); + while (itr != qmldirCache.constEnd()) { QmldirCache *cache = *itr; do { QmldirCache *nextCache = cache->next; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index f8c01ed876..ea9c2eafb5 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -44,9 +44,10 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qset.h> #include <QtCore/qstringlist.h> +#include <QtQml/qqmlerror.h> #include <private/qqmldirparser_p.h> -#include <private/qqmlmetatype_p.h> -#include <private/qhashedstring_p.h> +#include <private/qqmltype_p.h> +#include <private/qstringhash_p.h> // // W A R N I N G @@ -211,7 +212,9 @@ public: QQmlImportDatabase(QQmlEngine *); ~QQmlImportDatabase(); +#if QT_CONFIG(library) bool importDynamicPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, int vmaj, QList<QQmlError> *errors); +#endif QStringList importPathList(PathType type = LocalOrRemote) const; void setImportPathList(const QStringList &paths); diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 39da550d63..bc06226cbf 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -42,7 +42,6 @@ #include "qqmlincubator_p.h" #include "qqmlexpression_p.h" -#include "qqmlmemoryprofiler_p.h" #include "qqmlobjectcreator_p.h" void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) @@ -272,8 +271,6 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) if (!compilationUnit) return; - QML_MEMORY_SCOPE_URL(compilationUnit->finalUrl()); - QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this); QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this); diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 676ba1a29a..57ec8249cb 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -87,7 +87,7 @@ public: QPointer<QObject> result; QQmlGuardedContextData rootContext; QQmlEnginePrivate *enginePriv; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QScopedPointer<QQmlObjectCreator> creator; int subComponentToCreate; QQmlVMEGuard vmeGuard; diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp index 6322302422..2bfd2d5bb4 100644 --- a/src/qml/qml/qqmlinfo.cpp +++ b/src/qml/qml/qqmlinfo.cpp @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlDebug(const QObject *object) - \relates QQmlEngine + \relates QtQml \since 5.9 Prints debug messages that include the file and line number for the @@ -91,7 +91,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlInfo(const QObject *object) - \relates QQmlEngine + \relates QtQml Prints informational messages that include the file and line number for the specified QML \a object. @@ -119,7 +119,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlWarning(const QObject *object) - \relates QQmlEngine + \relates QtQml \since 5.9 Prints warning messages that include the file and line number for the diff --git a/src/qml/qml/qqmlinfo.h b/src/qml/qml/qqmlinfo.h index 673125632e..faa112d4af 100644 --- a/src/qml/qml/qqmlinfo.h +++ b/src/qml/qml/qqmlinfo.h @@ -40,6 +40,7 @@ #ifndef QQMLINFO_H #define QQMLINFO_H +#include <QtQml/qtqmlglobal.h> #include <QtCore/qdebug.h> #include <QtCore/qurl.h> #include <QtQml/qqmlerror.h> diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp new file mode 100644 index 0000000000..82cad8eba8 --- /dev/null +++ b/src/qml/qml/qqmlirloader.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlirloader_p.h" +#include <private/qqmlirbuilder_p.h> + +QT_BEGIN_NAMESPACE + +QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void QQmlIRLoader::load() +{ + output->jsGenerator.stringTable.initializeFromBackingUnit(unit); + + const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); + + for (quint32 i = 0; i < qmlUnit->nImports; ++i) + output->imports << qmlUnit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New<QmlIR::Pragma>(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + for (uint i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New<QmlIR::Object>(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + object->flags = serializedObject->flags; + object->id = serializedObject->id; + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector<int> functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New<QmlIR::Binding>(); + *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New<QmlIR::Signal>(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New<QmlIR::PoolList<QmlIR::Parameter> >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::Parameter *p = pool->New<QmlIR::Parameter>(); + *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + for (uint i = 0; i < serializedObject->nEnums; ++i) { + const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); + QmlIR::Enum *e = pool->New<QmlIR::Enum>(); + e->nameIndex = serializedEnum->nameIndex; + e->location = serializedEnum->location; + e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); + + for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { + QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); + *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); + e->enumValues->append(v); + } + + object->qmlEnums->append(e); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New<QmlIR::Property>(); + *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; + object->properties->append(p); + } + + { + const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); + for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { + QmlIR::Alias *a = pool->New<QmlIR::Alias>(); + *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; + object->aliases->append(a); + } + } + + const quint32_le *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New<QmlIR::Function>(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + f->index = functionIndices.count() - 1; + f->location = compiledFunction->location; + f->nameIndex = compiledFunction->nameIndex; + f->returnType = compiledFunction->returnType; + + f->formals.allocate(pool, int(compiledFunction->nFormals)); + const QV4::CompiledData::Parameter *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) + *static_cast<QV4::CompiledData::Parameter*>(&f->formals[i]) = *formalNameIdx; + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h new file mode 100644 index 0000000000..4812946ef0 --- /dev/null +++ b/src/qml/qml/qqmlirloader_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLIRLOADER_P_H +#define QQMLIRLOADER_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 <private/qtqmlglobal_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qqmljsmemorypool_p.h> + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +struct Object; +} + +struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { + QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + +QT_END_NAMESPACE + +#endif // QQMLIRLOADER_P_H diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9a3a5218e0..8661ebcc13 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -208,7 +208,10 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b } Q_ASSERT(m_qmlScope.valueRef()); - QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); + QV4::ReturnedValue res = v4Function->call( + &(callData->thisObject.asValue<QV4::Value>()), + callData->argValues<QV4::Value>(), callData->argc(), + static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); QV4::Scope scope(v4); QV4::ScopedValue result(scope, res); @@ -392,10 +395,10 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext, return; m_qmlScope.set(qmlContext->engine(), *qmlContext); m_v4Function = f; - setCompilationUnit(m_v4Function->compilationUnit); + setCompilationUnit(m_v4Function->executableCompilationUnit()); } -void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) { m_compilationUnit = compilationUnit; } diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 453c8ab8a8..eecee08062 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -153,7 +153,7 @@ protected: void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); - void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); + void setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); // We store some flag bits in the following flag pointers. // activeGuards:flag1 - notifyOnValueChanged @@ -178,11 +178,11 @@ private: QQmlJavaScriptExpression *m_nextExpression; QV4::PersistentValue m_qmlScope; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit; QV4::Function *m_v4Function; }; -class QQmlPropertyCapture +class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture { public: QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w) diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h index 04bab8d929..e182ced51d 100644 --- a/src/qml/qml/qqmllist_p.h +++ b/src/qml/qml/qqmllist_p.h @@ -52,7 +52,7 @@ // #include "qqmllist.h" -#include "qqmlpropertycache_p.h" +#include "qqmlmetaobject_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 2f769c1aef..5349572921 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qqmllistwrapper_p.h" -#include <private/qv8engine_p.h> #include <private/qqmllist_p.h> #include <private/qv4objectproto_p.h> #include <qv4objectiterator_p.h> diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 2b17037df0..dca13ac8d4 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -662,7 +662,7 @@ LOCALE_STRING_PROPERTY(exponential) LOCALE_STRING_PROPERTY(amText) LOCALE_STRING_PROPERTY(pmText) -class QV4LocaleDataDeletable : public QV8Engine::Deletable +class QV4LocaleDataDeletable : public QV4::ExecutionEngine::Deletable { public: QV4LocaleDataDeletable(QV4::ExecutionEngine *engine); diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h index ece06e04b4..ee5d9af2e7 100644 --- a/src/qml/qml/qqmlloggingcategory_p.h +++ b/src/qml/qml/qqmlloggingcategory_p.h @@ -65,7 +65,7 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) - Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1) + Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 12) public: enum DefaultLogLevel { diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp new file mode 100644 index 0000000000..a967f46b12 --- /dev/null +++ b/src/qml/qml/qqmlmetaobject.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlmetaobject_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> + +QT_BEGIN_NAMESPACE + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &staticQtMetaObject; } +}; + +static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, + const QByteArray &name) +{ + for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = resolvedMetaObject->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) + return true; + } + return false; +} + +static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = scopedName.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = scopedName.left(scopeIdx); + name = scopedName.mid(scopeIdx + 2); + } else { + name = scopedName; + } + + if (scope == "Qt") + return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name); + + if (isNamedEnumeratorInScope(metaObj, scope, name)) + return true; + + if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { + for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { + if (isNamedEnumeratorInScope(*related, scope, name)) + return true; + } + } + + return false; +} + +// 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()); + + struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) { + return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); + } }; + + const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2(); + if (tom == &QObject::staticMetaObject) return true; + + if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache + QQmlPropertyCache *fromp = from._m.asT1(); + QQmlPropertyCache *top = to._m.asT1(); + + while (fromp) { + if (fromp == top) return true; + fromp = fromp->parent(); + } + } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject + QQmlPropertyCache *fromp = from._m.asT1(); + + while (fromp) { + const QMetaObject *fromm = fromp->metaObject(); + if (fromm && I::equal(fromm, tom)) return true; + fromp = fromp->parent(); + } + } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache + const QMetaObject *fromm = from._m.asT2(); + + if (!tom) return false; + + while (fromm) { + if (I::equal(fromm, tom)) return true; + fromm = fromm->superClass(); + } + } else { // QMetaObject -> QMetaObject + const QMetaObject *fromm = from._m.asT2(); + + while (fromm) { + if (I::equal(fromm, tom)) return true; + fromm = fromm->superClass(); + } + } + + return false; +} + +void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) +{ + int offset; + + switch (type) { + case QMetaObject::ReadProperty: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyUser: + offset = (*metaObject)->propertyOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->propertyOffset(); + } + break; + case QMetaObject::InvokeMetaMethod: + offset = (*metaObject)->methodOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->methodOffset(); + } + break; + default: + offset = 0; + Q_UNIMPLEMENTED(); + offset = INT_MAX; + } + + *index -= offset; +} + +QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const +{ + if (_m.isNull()) return nullptr; + if (_m.isT1()) return _m.asT1(); + else return e->cache(_m.asT2()); +} + +int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const +{ + Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); + + int type = data.propType(); + + const char *propTypeName = nullptr; + + if (type == QMetaType::UnknownType) { + // Find the return type name from the method info + QMetaMethod m; + + if (_m.isT1()) { + QQmlPropertyCache *c = _m.asT1(); + Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); + + while (data.coreIndex() < c->methodIndexCacheStart) + c = c->_parent; + + const QMetaObject *metaObject = c->createMetaObject(); + Q_ASSERT(metaObject); + m = metaObject->method(data.coreIndex()); + } else { + m = _m.asT2()->method(data.coreIndex()); + } + + type = m.returnType(); + propTypeName = m.typeName(); + } + + if (QMetaType::sizeOf(type) <= int(sizeof(int))) { + if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) + return QMetaType::Int; + + if (isNamedEnumerator(metaObject(), propTypeName)) + return QMetaType::Int; + + if (type == QMetaType::UnknownType) { + if (unknownTypeError) + *unknownTypeError = propTypeName; + } + } // else we know that it's a known type, as sizeOf(UnknownType) == 0 + + return type; +} + +int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(!_m.isNull() && index >= 0); + + if (_m.isT1()) { + typedef QQmlPropertyCacheMethodArguments A; + + QQmlPropertyCache *c = _m.asT1(); + Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); + + while (index < c->methodIndexCacheStart) + c = c->_parent; + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); + + if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) + return static_cast<A *>(rv->arguments())->arguments; + + const QMetaObject *metaObject = c->createMetaObject(); + Q_ASSERT(metaObject); + QMetaMethod m = metaObject->method(index); + + int argc = m.parameterCount(); + if (!rv->arguments()) { + A *args = c->createArgumentsObject(argc, m.parameterNames()); + rv->setArguments(args); + } + A *args = static_cast<A *>(rv->arguments()); + + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType){ + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + + } + args->arguments[ii + 1] = type; + } + args->argumentsValid = true; + return static_cast<A *>(rv->arguments())->arguments; + + } else { + QMetaMethod m = _m.asT2()->method(index); + return methodParameterTypes(m, argStorage, unknownTypeError); + + } +} + +int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(argStorage); + + int argc = m.parameterCount(); + argStorage->resize(argc + 1); + argStorage->operator[](0) = argc; + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType) { + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + } + argStorage->operator[](ii + 1) = type; + } + + return argStorage->data(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h new file mode 100644 index 0000000000..65d6361b90 --- /dev/null +++ b/src/qml/qml/qqmlmetaobject_p.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMETAOBJECT_P_H +#define QQMLMETAOBJECT_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/qtqmlglobal.h> + +#include <private/qflagpointer_p.h> +#include <private/qqmlpropertycache_p.h> + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmetaobject.h> + +QT_BEGIN_NAMESPACE + +// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. +// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but +// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a +// QObject type used in assignment or when we don't have a QQmlEngine etc. +// +// This class does NOT reference the propertycache. +class QQmlEnginePrivate; +class QQmlPropertyData; +class Q_QML_EXPORT QQmlMetaObject +{ +public: + typedef QVarLengthArray<int, 9> ArgTypeStorage; + + inline QQmlMetaObject(); + inline QQmlMetaObject(QObject *); + inline QQmlMetaObject(const QMetaObject *); + inline QQmlMetaObject(QQmlPropertyCache *); + inline QQmlMetaObject(const QQmlMetaObject &); + + inline QQmlMetaObject &operator=(const QQmlMetaObject &); + + inline bool isNull() const; + + inline const char *className() const; + inline int propertyCount() const; + + inline bool hasMetaObject() const; + inline const QMetaObject *metaObject() const; + + QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; + + int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; + int *methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; + + static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); + + // 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); + +protected: + QBiPointer<QQmlPropertyCache, const QMetaObject> _m; + int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; + +}; + +QQmlMetaObject::QQmlMetaObject() +{ +} + +QQmlMetaObject::QQmlMetaObject(QObject *o) +{ + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (ddata && ddata->propertyCache) _m = ddata->propertyCache; + else _m = o->metaObject(); + } +} + +QQmlMetaObject::QQmlMetaObject(const QMetaObject *m) + : _m(m) +{ +} + +QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m) + : _m(m) +{ +} + +QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o) + : _m(o._m) +{ +} + +QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o) +{ + _m = o._m; + return *this; +} + +bool QQmlMetaObject::isNull() const +{ + return _m.isNull(); +} + +const char *QQmlMetaObject::className() const +{ + if (_m.isNull()) { + return nullptr; + } else if (_m.isT1()) { + return _m.asT1()->className(); + } else { + return _m.asT2()->className(); + } +} + +int QQmlMetaObject::propertyCount() const +{ + if (_m.isNull()) { + return 0; + } else if (_m.isT1()) { + return _m.asT1()->propertyCount(); + } else { + return _m.asT2()->propertyCount(); + } +} + +bool QQmlMetaObject::hasMetaObject() const +{ + return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject()); +} + +const QMetaObject *QQmlMetaObject::metaObject() const +{ + if (_m.isNull()) return nullptr; + if (_m.isT1()) return _m.asT1()->createMetaObject(); + else return _m.asT2(); +} + +QT_END_NAMESPACE + +#endif // QQMLMETAOBJECT_P_H diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 0f8a850584..c2674b402a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -37,410 +37,78 @@ ** ****************************************************************************/ -#include <QtQml/qqmlprivate.h> #include "qqmlmetatype_p.h" -#include <private/qqmlproxymetaobject_p.h> -#include <private/qqmlcustomparser_p.h> -#include <private/qhashedstring_p.h> -#include <private/qqmlimport_p.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qmetaobject.h> -#include <QtCore/qbitarray.h> -#include <QtCore/qreadwritelock.h> -#include <QtCore/private/qmetaobject_p.h> -#include <QtCore/qloggingcategory.h> - -#include <qmetatype.h> -#include <qobjectdefs.h> -#include <qbytearray.h> -#include <qreadwritelock.h> -#include <qstring.h> -#include <qstringlist.h> -#include <qvector.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmltypemodule_p_p.h> +#include <private/qqmltype_p_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmlextensionplugin_p.h> +#include <private/qv4executablecompilationunit_p.h> -#include <ctype.h> -#include "qqmlcomponent.h" +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmutex.h> +#include <QtCore/qloggingcategory.h> Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) QT_BEGIN_NAMESPACE -struct QQmlMetaTypeData +struct LockedData : private QQmlMetaTypeData { - QQmlMetaTypeData(); - ~QQmlMetaTypeData(); - void registerType(QQmlTypePrivate *priv); - QList<QQmlType> types; - QSet<QQmlType> undeletableTypes; - typedef QHash<int, QQmlTypePrivate *> Ids; - Ids idToType; - typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names; - Names nameToType; - typedef QHash<QUrl, 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 - // of them by url, even if it was registered as - // a module via QQmlPrivate::RegisterCompositeType - typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects; - MetaObjects metaObjectToType; - typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; - StringConverters stringConverters; - - struct VersionedUri { - VersionedUri() - : majorVersion(0) {} - VersionedUri(const QHashedString &uri, int majorVersion) - : uri(uri), majorVersion(majorVersion) {} - bool operator==(const VersionedUri &other) const { - return other.majorVersion == majorVersion && other.uri == uri; - } - QHashedString uri; - int majorVersion; - }; - typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules; - TypeModules uriToModule; - - QBitArray objects; - QBitArray interfaces; - QBitArray lists; - - QList<QQmlPrivate::AutoParentFunction> parentFunctions; - QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit; - - QSet<QString> protectedNamespaces; - - QString typeRegistrationNamespace; - - QHash<int, int> qmlLists; - - QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches; - QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion); - QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); - - void startRecordingTypeRegFailures(QStringList *storage) - { typeRegistrationFailures = storage; } - void stopRecordingTypeRegFailures() - { startRecordingTypeRegFailures(nullptr); } - void recordTypeRegFailure(const QString &message) - { - if (typeRegistrationFailures) - typeRegistrationFailures->append(message); - else - qWarning("%s", message.toUtf8().constData()); - } - -private: - QStringList *typeRegistrationFailures = nullptr; + friend class QQmlMetaTypeDataPtr; }; -struct EnumInfo { - QStringList path; - QString metaObjectName; - QString enumName; - QString enumKey; - QString metaEnumScope; - bool scoped; -}; +Q_GLOBAL_STATIC(LockedData, metaTypeData) +Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) -class QQmlTypeModulePrivate +class QQmlMetaTypeDataPtr { + Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) public: - QQmlTypeModulePrivate() - : minMinorVersion(INT_MAX), maxMinorVersion(0), locked(false) {} - - static QQmlTypeModulePrivate* get(QQmlTypeModule* q) { return q->d; } + QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {} + ~QQmlMetaTypeDataPtr() = default; - QQmlMetaTypeData::VersionedUri uri; + QQmlMetaTypeData &operator*() { return *data; } + QQmlMetaTypeData *operator->() { return data; } + operator QQmlMetaTypeData *() { return data; } - int minMinorVersion; - int maxMinorVersion; - bool locked; + const QQmlMetaTypeData &operator*() const { return *data; } + const QQmlMetaTypeData *operator->() const { return data; } + operator const QQmlMetaTypeData *() const { return data; } - void add(QQmlTypePrivate *); - void remove(const QQmlTypePrivate *type); - - typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash; - TypeHash typeHash; -}; - -Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, metaTypeDataLock, (QMutex::Recursive)) - -static uint qHash(const QQmlMetaTypeData::VersionedUri &v) -{ - return v.uri.hash() ^ qHash(v.majorVersion); -} + bool isValid() const { return data != nullptr; } -QQmlMetaTypeRegistrationFailureRecorder::QQmlMetaTypeRegistrationFailureRecorder() -{ - metaTypeData()->startRecordingTypeRegFailures(&_failures); -} - -QQmlMetaTypeRegistrationFailureRecorder::~QQmlMetaTypeRegistrationFailureRecorder() -{ - metaTypeData()->stopRecordingTypeRegFailures(); -} - -QQmlMetaTypeData::QQmlMetaTypeData() -{ -} - -QQmlMetaTypeData::~QQmlMetaTypeData() -{ - for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) - delete *i; - for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); - it != end; ++it) - (*it)->release(); -} - -class QQmlTypePrivate -{ - Q_DISABLE_COPY(QQmlTypePrivate) -public: - QQmlTypePrivate(QQmlType::RegistrationType type); - ~QQmlTypePrivate(); - - void init() const; - void initEnums(const QQmlPropertyCache *cache = nullptr) const; - void insertEnums(const QMetaObject *metaObject) const; - void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; - - QAtomicInt refCount; - QQmlType::RegistrationType regType; - - struct QQmlCppTypeData - { - int allocationSize; - void (*newFunc)(void *); - QString noCreationReason; - int parserStatusCast; - QObject *(*extFunc)(QObject *); - const QMetaObject *extMetaObject; - QQmlCustomParser *customParser; - QQmlAttachedPropertiesFunc attachedPropertiesFunc; - const QMetaObject *attachedPropertiesType; - int propertyValueSourceCast; - int propertyValueInterceptorCast; - bool registerEnumClassesUnscoped; - }; - - struct QQmlSingletonTypeData - { - QQmlType::SingletonInstanceInfo *singletonInstanceInfo; - }; - - struct QQmlCompositeTypeData - { - QUrl url; - }; - - union extraData { - QQmlCppTypeData* cd; - QQmlSingletonTypeData* sd; - QQmlCompositeTypeData* fd; - } extraData; - - const char *iid; - QHashedString module; - QString name; - QString elementName; - int version_maj; - int version_min; - int typeId; - int listId; - int revision; - mutable bool containsRevisionedAttributes; - mutable QQmlType superType; - const QMetaObject *baseMetaObject; - - int index; - mutable volatile bool isSetup:1; - mutable volatile bool isEnumFromCacheSetup:1; - mutable volatile bool isEnumFromBaseSetup:1; - mutable bool haveSuperType:1; - 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; - - struct PropertyCacheByMinorVersion - { - PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {} - explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {} - QQmlPropertyCachePtr cache; - int minorVersion; - }; - QVector<PropertyCacheByMinorVersion> propertyCaches; - QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const; - void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache); private: - void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const; - void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const; + QMutexLocker locker; + LockedData *data = nullptr; }; -void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, + const QQmlPrivate::RegisterInterface &type) { - for (int i = 0; i < types.count(); ++i) { - if (!types.at(i).isValid()) { - types[i] = QQmlType(priv); - priv->index = i; - return; - } - } - types.append(QQmlType(priv)); - priv->index = types.count() - 1; -} - -void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) -{ - if (scriptCallback && scriptApi(e).isUndefined()) { - QJSValue value = scriptCallback(e, e); - if (value.isQObject()) { - QObject *o = value.toQObject(); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } - setScriptApi(e, value); - } else if (qobjectCallback && !qobjectApi(e)) { - QObject *o = qobjectCallback(e, e); - setQObjectApi(e, o); - if (!o) { - qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", qPrintable(typeName)); - } - // if this object can use a property cache, create it now - QQmlData::ensurePropertyCache(e, o); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } else if (!url.isEmpty() && !qobjectApi(e)) { - QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); - QObject *o = component.beginCreate(e->rootContext()); - setQObjectApi(e, o); - if (o) - component.completeCreate(); - } -} - -void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) -{ - // cleans up the engine-specific singleton instances if they exist. - scriptApis.remove(e); - QObject *o = qobjectApis.take(e); - if (o) { - QQmlData *ddata = QQmlData::get(o, false); - if (url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) - return; - delete o; - } -} - -void QQmlType::SingletonInstanceInfo::setQObjectApi(QQmlEngine *e, QObject *o) -{ - qobjectApis.insert(e, o); -} - -QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const -{ - return qobjectApis.value(e); -} - -void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v) -{ - scriptApis.insert(e, v); -} - -QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const -{ - return scriptApis.value(e); -} - -QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) -: refCount(1), regType(type), iid(nullptr), typeId(0), listId(0), revision(0), - containsRevisionedAttributes(false), baseMetaObject(nullptr), - index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false), - haveSuperType(false) -{ - switch (type) { - case QQmlType::CppType: - extraData.cd = new QQmlCppTypeData; - extraData.cd->allocationSize = 0; - extraData.cd->newFunc = 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->registerEnumClassesUnscoped = true; - break; - case QQmlType::SingletonType: - case QQmlType::CompositeSingletonType: - extraData.sd = new QQmlSingletonTypeData; - extraData.sd->singletonInstanceInfo = nullptr; - break; - case QQmlType::InterfaceType: - extraData.cd = nullptr; - break; - case QQmlType::CompositeType: - extraData.fd = new QQmlCompositeTypeData; - break; - default: qFatal("QQmlTypePrivate Internal Error."); - } -} - -QQmlTypePrivate::~QQmlTypePrivate() -{ - qDeleteAll(scopedEnums); - switch (regType) { - case QQmlType::CppType: - delete extraData.cd->customParser; - delete extraData.cd; - break; - case QQmlType::SingletonType: - case QQmlType::CompositeSingletonType: - delete extraData.sd->singletonInstanceInfo; - delete extraData.sd; - break; - case QQmlType::CompositeType: - delete extraData.fd; - break; - default: //Also InterfaceType, because it has no extra data - break; - } -} - -QQmlType::QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &interface) - : d(new QQmlTypePrivate(InterfaceType)) -{ - d->iid = interface.iid; - d->typeId = interface.typeId; - d->listId = interface.listId; + auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); + d->iid = type.iid; + d->typeId = type.typeId; + d->listId = type.listId; d->isSetup = true; d->version_maj = 0; d->version_min = 0; data->registerType(d); + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) - : d(new QQmlTypePrivate(SingletonType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterSingletonType &type) { + auto *d = new QQmlTypePrivate(QQmlType::SingletonType); data->registerType(d); - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); - + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; - if (type.qobjectApi) { + if (type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi)) { if (type.version >= 1) // static metaobject added in version 1 d->baseMetaObject = type.instanceMetaObject; if (type.version >= 2) // typeId added in version 2 @@ -449,37 +117,26 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm d->revision = type.revision; } - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; + d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; - d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + if (type.version >= 3) { + d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.generalizedQobjectApi; + } else { + d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + } d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); d->extraData.sd->singletonInstanceInfo->instanceMetaObject - = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : nullptr; -} - -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type) - : d(new QQmlTypePrivate(CompositeSingletonType)) -{ - data->registerType(d); - - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); - - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; + = ((type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi) ) && type.version >= 1) ? type.instanceMetaObject : nullptr; - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; - d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url); - d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterType &type) - : d(new QQmlTypePrivate(CppType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterType &type) { + QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType); data->registerType(d); - - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; @@ -509,141 +166,41 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) d->extraData.cd->registerEnumClassesUnscoped = false; } + + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) - : d(new QQmlTypePrivate(CompositeType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterCompositeType &type) { + auto *d = new QQmlTypePrivate(QQmlType::CompositeType); data->registerType(d); - - d->elementName = elementName; - - d->module = QString::fromUtf8(type.uri); + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; d->extraData.fd->url = QQmlTypeLoader::normalize(type.url); + return d; } -QQmlType::QQmlType() - : d(nullptr) -{ -} - -QQmlType::QQmlType(const QQmlType &other) - : d(other.d) -{ - if (d) - d->refCount.ref(); -} - -QQmlType &QQmlType::operator =(const QQmlType &other) -{ - if (d != other.d) { - if (d && !d->refCount.deref()) - delete d; - d = other.d; - if (d) - d->refCount.ref(); - } - return *this; -} - -QQmlType::QQmlType(QQmlTypePrivate *priv) - : d(priv) -{ - if (d) - d->refCount.ref(); -} - -QQmlType::~QQmlType() -{ - if (d && !d->refCount.deref()) - delete d; -} - -QHashedString QQmlType::module() const -{ - if (!d) - return QHashedString(); - return d->module; -} - -int QQmlType::majorVersion() const -{ - if (!d) - return -1; - return d->version_maj; -} - -int QQmlType::minorVersion() const +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterCompositeSingletonType &type) { - if (!d) - return -1; - return d->version_min; -} - -bool QQmlType::availableInVersion(int vmajor, int vminor) const -{ - Q_ASSERT(vmajor >= 0 && vminor >= 0); - if (!d) - return false; - return vmajor == d->version_maj && vminor >= d->version_min; -} - -bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const -{ - Q_ASSERT(vmajor >= 0 && vminor >= 0); - if (!d) - return false; - return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; -} - -// returns the nearest _registered_ super class -QQmlType QQmlType::superType() const -{ - if (!d) - return QQmlType(); - if (!d->haveSuperType && d->baseMetaObject) { - const QMetaObject *mo = d->baseMetaObject->superClass(); - while (mo && !d->superType.isValid()) { - d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min); - mo = mo->superClass(); - } - d->haveSuperType = true; - } - - return d->superType; -} + auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); + data->registerType(d); + d->setName(QString::fromUtf8(type.uri), elementName); -QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const -{ - Q_ASSERT(isComposite()); - if (!engine || !d) - return QQmlType(); - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); - if (td.isNull() || !td->isComplete()) - return QQmlType(); - QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); - const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - return QQmlMetaType::qmlType(mo); -} + d->version_maj = type.versionMajor; + d->version_min = type.versionMinor; -QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const -{ - // similar logic to resolveCompositeBaseType - Q_ASSERT(isComposite()); - if (!engine) - return nullptr; - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); - if (td.isNull() || !td->isComplete()) - return nullptr; - QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); - return compilationUnit->rootPropertyCache().data(); + 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); + return d; } -static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, - const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) +void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) { // Set classname builder.setClassName(ignoreEnd->className()); @@ -710,918 +267,10 @@ static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } -static bool isPropertyRevisioned(const QMetaObject *mo, int index) -{ - int i = index; - i -= mo->propertyOffset(); - if (i < 0 && mo->d.superdata) - return isPropertyRevisioned(mo->d.superdata, index); - - const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data); - if (i >= 0 && i < mop->propertyCount) { - int handle = mop->propertyData + 3*i; - int flags = mo->d.data[handle + 2]; - - return (flags & Revisioned); - } - - return false; -} - -void QQmlTypePrivate::init() const -{ - if (isSetup) - return; - - QMutexLocker lock(metaTypeDataLock()); - if (isSetup) - return; - - const QMetaObject *mo = baseMetaObject; - if (!mo) { - // version 0 singleton type without metaobject information - return; - } - - if (regType == QQmlType::CppType) { - // Setup extended meta object - // XXX - very inefficient - if (extraData.cd->extFunc) { - QMetaObjectBuilder builder; - clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, extraData.cd->extMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = mo; - QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 }; - metaObjects << data; - } - } - - mo = mo->d.superdata; - while(mo) { - QQmlTypePrivate *t = metaTypeData()->metaObjectToType.value(mo); - if (t) { - if (t->regType == QQmlType::CppType) { - if (t->extraData.cd->extFunc) { - QMetaObjectBuilder builder; - clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = baseMetaObject; - if (!metaObjects.isEmpty()) - metaObjects.constLast().metaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; - metaObjects << data; - } - } - } - mo = mo->d.superdata; - } - - for (int ii = 0; ii < metaObjects.count(); ++ii) { - metaObjects[ii].propertyOffset = - metaObjects.at(ii).metaObject->propertyOffset(); - metaObjects[ii].methodOffset = - metaObjects.at(ii).metaObject->methodOffset(); - } - - // Check for revisioned details - { - const QMetaObject *mo = nullptr; - if (metaObjects.isEmpty()) - mo = baseMetaObject; - else - mo = metaObjects.constFirst().metaObject; - - for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { - if (isPropertyRevisioned(mo, ii)) - containsRevisionedAttributes = true; - } - - for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { - if (mo->method(ii).revision() != 0) - containsRevisionedAttributes = true; - } - } - - isSetup = true; - lock.unlock(); -} - -void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const -{ - if ((isEnumFromBaseSetup || !baseMetaObject) - && (isEnumFromCacheSetup || !cache)) { - return; - } - - init(); - - QMutexLocker lock(metaTypeDataLock()); - - if (!isEnumFromCacheSetup && cache) { - insertEnumsFromPropertyCache(cache); - isEnumFromCacheSetup = true; - } - - if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject - insertEnums(baseMetaObject); - isEnumFromBaseSetup = true; - } -} - -void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const -{ - // Add any enum values defined by 'related' classes - if (metaObject->d.relatedMetaObjects) { - const auto *related = metaObject->d.relatedMetaObjects; - if (related) { - while (*related) - insertEnums(*related++); - } - } - - QSet<QString> localEnums; - const QMetaObject *localMetaObject = nullptr; - - // 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); - const bool isScoped = e.isScoped(); - QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr; - - // We allow enums in sub-classes to overwrite enums from base-classes, such as - // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin). - // This is acceptable because the _use_ of the enum from the QML side requires qualification - // anyway, i.e. ListView.Center vs. Item.Center. - // However if a class defines two enums with the same value, then that must produce a warning - // because it represents a valid conflict. - if (e.enclosingMetaObject() != localMetaObject) { - localEnums.clear(); - localMetaObject = e.enclosingMetaObject(); - } - - 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 (localEnums.contains(key)) { - auto existingEntry = enums.find(key); - if (existingEntry != 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); - } - if (isScoped) - scoped->insert(key, value); - } - - if (isScoped) { - scopedEnums << scoped; - scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1); - } - } -} - -void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const -{ - path.append(QString::fromUtf8(metaObject->className())); - - if (metaObject->d.relatedMetaObjects) { - const auto *related = metaObject->d.relatedMetaObjects; - if (related) { - while (*related) - createListOfPossibleConflictingItems(*related++, enumInfoList, path); - } - } - - for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { - const auto e = metaObject->enumerator(ii); - - for (int jj = 0; jj < e.keyCount(); ++jj) { - const QString key = QString::fromUtf8(e.key(jj)); - - EnumInfo enumInfo; - enumInfo.metaObjectName = QString::fromUtf8(metaObject->className()); - enumInfo.enumName = QString::fromUtf8(e.name()); - enumInfo.enumKey = key; - enumInfo.scoped = e.isScoped(); - enumInfo.path = path; - enumInfo.metaEnumScope = QString::fromUtf8(e.scope()); - enumInfoList.append(enumInfo); - } - } -} - -void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const -{ - QList<EnumInfo> enumInfoList; - - if (baseMetaObject) // prefer baseMetaObject if available - metaObject = baseMetaObject; - - if (!metaObject) { // If there is no metaObject at all return early - qWarning() << "No meta object information available. Skipping conflict analysis."; - return; - } - - createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList()); - - qWarning().noquote() << QLatin1String("Possible conflicting items:"); - // find items with conflicting key - for (const auto i : enumInfoList) { - if (i.enumKey == conflictingKey) - qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope " - << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->")); - } -} - -void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const -{ - const QMetaObject *cppMetaObject = cache->firstCppMetaObject(); - - while (cache && cache->metaObject() != cppMetaObject) { - - int count = cache->qmlEnumCount(); - for (int ii = 0; ii < count; ++ii) { - QStringHash<int> *scoped = new QStringHash<int>(); - QQmlEnumData *enumData = cache->qmlEnum(ii); - - for (int jj = 0; jj < enumData->values.count(); ++jj) { - const QQmlEnumValue &value = enumData->values.at(jj); - enums.insert(value.namedValue, value.value); - scoped->insert(value.namedValue, value.value); - } - scopedEnums << scoped; - scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1); - } - cache = cache->parent(); - } - insertEnums(cppMetaObject); -} - - -QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersion) const -{ - for (int i = 0; i < propertyCaches.count(); ++i) - if (propertyCaches.at(i).minorVersion == minorVersion) - return propertyCaches.at(i).cache.data(); - return nullptr; -} - -void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache) -{ - for (int i = 0; i < propertyCaches.count(); ++i) { - if (propertyCaches.at(i).minorVersion == minorVersion) { - propertyCaches[i].cache = cache; - return; - } - } - propertyCaches.append(PropertyCacheByMinorVersion(cache, minorVersion)); -} - -QByteArray QQmlType::typeName() const -{ - if (d) { - if (d->regType == SingletonType || d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); - else if (d->baseMetaObject) - return d->baseMetaObject->className(); - } - return QByteArray(); -} - -QString QQmlType::elementName() const -{ - if (!d) - return QString(); - return d->elementName; -} - -QString QQmlType::qmlTypeName() const -{ - if (!d) - return QString(); - if (d->name.isEmpty()) { - if (!d->module.isEmpty()) - d->name = static_cast<QString>(d->module) + QLatin1Char('/') + d->elementName; - else - d->name = d->elementName; - } - - return d->name; -} - -QObject *QQmlType::create() const -{ - if (!d || !isCreatable()) - return nullptr; - - d->init(); - - QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize); - d->extraData.cd->newFunc(rv); - - if (rv && !d->metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->metaObjects); - - return rv; -} - -void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const -{ - if (!d || !isCreatable()) - return; - - d->init(); - - QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory); - d->extraData.cd->newFunc(rv); - - if (rv && !d->metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->metaObjects); - - *out = rv; - *memory = ((char *)rv) + d->extraData.cd->allocationSize; -} - -QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const -{ - if (!d) - return nullptr; - if (d->regType != SingletonType && d->regType != CompositeSingletonType) - return nullptr; - return d->extraData.sd->singletonInstanceInfo; -} - -QQmlCustomParser *QQmlType::customParser() const -{ - if (!d) - return nullptr; - if (d->regType != CppType) - return nullptr; - return d->extraData.cd->customParser; -} - -QQmlType::CreateFunc QQmlType::createFunction() const -{ - if (!d || d->regType != CppType) - return nullptr; - return d->extraData.cd->newFunc; -} - -QString QQmlType::noCreationReason() const -{ - if (!d || d->regType != CppType) - return QString(); - return d->extraData.cd->noCreationReason; -} - -bool QQmlType::isCreatable() const -{ - return d && d->regType == CppType && d->extraData.cd->newFunc; -} - -QQmlType::ExtensionFunc QQmlType::extensionFunction() const -{ - if (!d || d->regType != CppType) - return nullptr; - return d->extraData.cd->extFunc; -} - -bool QQmlType::isExtendedType() const -{ - if (!d) - return false; - d->init(); - - return !d->metaObjects.isEmpty(); -} - -bool QQmlType::isSingleton() const -{ - return d && (d->regType == SingletonType || d->regType == CompositeSingletonType); -} - -bool QQmlType::isInterface() const -{ - return d && d->regType == InterfaceType; -} - -bool QQmlType::isComposite() const -{ - return d && (d->regType == CompositeType || d->regType == CompositeSingletonType); -} - -bool QQmlType::isCompositeSingleton() const -{ - return d && d->regType == CompositeSingletonType; -} - -int QQmlType::typeId() const -{ - return d ? d->typeId : -1; -} - -int QQmlType::qListTypeId() const -{ - return d ? d->listId : -1; -} - -const QMetaObject *QQmlType::metaObject() const -{ - if (!d) - return nullptr; - d->init(); - - if (d->metaObjects.isEmpty()) - return d->baseMetaObject; - else - return d->metaObjects.constFirst().metaObject; - -} - -const QMetaObject *QQmlType::baseMetaObject() const -{ - return d ? d->baseMetaObject : nullptr; -} - -bool QQmlType::containsRevisionedAttributes() const -{ - if (!d) - return false; - d->init(); - - return d->containsRevisionedAttributes; -} - -int QQmlType::metaObjectRevision() const -{ - return d ? d->revision : -1; -} - -QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const -{ - if (!d) - return nullptr; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesFunc; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesFunction(engine); -} - -const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const -{ - if (!d) - return nullptr; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesType; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesType(engine); -} - -/* -This is the id passed to qmlAttachedPropertiesById(). This is different from the index -for the case that a single class is registered under two or more names (eg. Item in -Qt 4.7 and QtQuick 1.0). -*/ -int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const -{ - if (!d) - return -1; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesType ? d->index : -1; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesId(engine); -} - -int QQmlType::parserStatusCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->parserStatusCast; -} - -int QQmlType::propertyValueSourceCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->propertyValueSourceCast; -} - -int QQmlType::propertyValueInterceptorCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->propertyValueInterceptorCast; -} - -const char *QQmlType::interfaceIId() const -{ - if (!d || d->regType != InterfaceType) - return nullptr; - return d->iid; -} - -int QQmlType::index() const -{ - return d ? d->index : -1; -} - -QUrl QQmlType::sourceUrl() const -{ - if (d) { - if (d->regType == CompositeType) - return d->extraData.fd->url; - else if (d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->url; - } - return QUrl(); -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -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.count()); - int *rv = d->scopedEnums.at(index)->value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -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.count()); - int *rv = d->scopedEnums.at(index)->value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); - if (rv) { - int index = *rv; - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length())); - if (rv) - return *rv; - } - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); - if (rv) { - int index = *rv; - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - rv = d->scopedEnums.at(index)->value(QHashedStringRef(name)); - if (rv) - return *rv; - } - } - - *ok = false; - return -1; -} - -void QQmlType::refHandle(QQmlTypePrivate *priv) -{ - if (priv) - priv->refCount.ref(); -} - -void QQmlType::derefHandle(QQmlTypePrivate *priv) -{ - if (priv && !priv->refCount.deref()) - delete priv; -} - -int QQmlType::refCount(QQmlTypePrivate *priv) -{ - if (priv) - return priv->refCount; - return -1; -} - -namespace { -template <typename QQmlTypeContainer> -void removeQQmlTypePrivate(QQmlTypeContainer &container, const QQmlTypePrivate *reference) -{ - for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) { - if (*it == reference) - it = container.erase(it); - else - ++it; - } -} - -struct IsQQmlTypePrivate -{ - const QQmlTypePrivate *reference; - explicit IsQQmlTypePrivate(const QQmlTypePrivate *ref) : reference(ref) {} - - bool operator()(const QQmlTypePrivate *priv) const { return reference == priv; } -}; -} - -QQmlTypeModule::QQmlTypeModule() -: d(new QQmlTypeModulePrivate) -{ -} - -QQmlTypeModule::~QQmlTypeModule() -{ - delete d; d = nullptr; -} - -QString QQmlTypeModule::module() const -{ - return d->uri.uri; -} - -int QQmlTypeModule::majorVersion() const -{ - return d->uri.majorVersion; -} - -int QQmlTypeModule::minimumMinorVersion() const -{ - return d->minMinorVersion; -} - -int QQmlTypeModule::maximumMinorVersion() const -{ - return d->maxMinorVersion; -} - -void QQmlTypeModulePrivate::add(QQmlTypePrivate *type) -{ - int minVersion = type->version_min; - minMinorVersion = qMin(minMinorVersion, minVersion); - maxMinorVersion = qMax(maxMinorVersion, minVersion); - - QList<QQmlTypePrivate *> &list = typeHash[type->elementName]; - for (int ii = 0; ii < list.count(); ++ii) { - Q_ASSERT(list.at(ii)); - if (list.at(ii)->version_min < minVersion) { - list.insert(ii, type); - return; - } - } - list.append(type); -} - -void QQmlTypeModulePrivate::remove(const QQmlTypePrivate *type) -{ - for (TypeHash::ConstIterator elementIt = typeHash.begin(); elementIt != typeHash.end();) { - QList<QQmlTypePrivate *> &list = const_cast<QList<QQmlTypePrivate *> &>(elementIt.value()); - - removeQQmlTypePrivate(list, type); - -#if 0 - if (list.isEmpty()) - elementIt = typeHash.erase(elementIt); - else - ++elementIt; -#else - ++elementIt; -#endif - } -} - -QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const -{ - QMutexLocker lock(metaTypeDataLock()); - - QList<QQmlTypePrivate *> *types = d->typeHash.value(name); - if (types) { - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->version_min <= minor) - return QQmlType(types->at(ii)); - } - - return QQmlType(); -} - -QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const -{ - QMutexLocker lock(metaTypeDataLock()); - - QList<QQmlTypePrivate *> *types = d->typeHash.value(name); - if (types) { - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->version_min <= minor) - return QQmlType(types->at(ii)); - } - - return QQmlType(); -} - -void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const -{ - QMutexLocker lock(metaTypeDataLock()); - for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end(); - typeCandidates != end; ++typeCandidates) { - for (auto type: typeCandidates.value()) { - if (type->regType == QQmlType::CompositeSingletonType) - callback(QQmlType(type)); - } - } -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion() -: m_module(nullptr), m_minor(0) -{ -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor) -: m_module(module), m_minor(minor) -{ - Q_ASSERT(m_module); - Q_ASSERT(m_minor >= 0); -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o) -: m_module(o.m_module), m_minor(o.m_minor) -{ -} - -QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o) -{ - m_module = o.m_module; - m_minor = o.m_minor; - return *this; -} - -QQmlTypeModule *QQmlTypeModuleVersion::module() const -{ - return m_module; -} - -int QQmlTypeModuleVersion::minorVersion() const -{ - return m_minor; -} - -QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const -{ - if (!m_module) - return QQmlType(); - return m_module->type(name, m_minor); -} - -QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const -{ - if (!m_module) - return QQmlType(); - return m_module->type(name, m_minor); -} - -void qmlClearTypeRegistrations() // Declared in qqml.h +void QQmlMetaType::clearTypeRegistrations() { //Only cleans global static, assumed no running engine - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i) delete *i; @@ -1630,44 +279,35 @@ void qmlClearTypeRegistrations() // Declared in qqml.h data->idToType.clear(); data->nameToType.clear(); data->urlToType.clear(); + data->typePropertyCaches.clear(); data->urlToNonFileImportType.clear(); data->metaObjectToType.clear(); data->uriToModule.clear(); data->undeletableTypes.clear(); - - QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types -#if QT_CONFIG(library) - qmlClearEnginePlugins(); -#endif -} - -static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) -{ - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - data->parentFunctions.removeOne(function); } -static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) +int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->parentFunctions.append(autoparent.function); return data->parentFunctions.count() - 1; } -QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface) +void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) { - if (interface.version > 0) - qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; + data->parentFunctions.removeOne(function); +} - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); +QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) +{ + if (type.version > 0) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); - QQmlType type(data, interface); - QQmlTypePrivate *priv = type.priv(); + QQmlMetaTypeDataPtr data; + QQmlTypePrivate *priv = createQQmlType(data, type); Q_ASSERT(priv); data->idToType.insert(priv->typeId, priv); @@ -1676,14 +316,14 @@ QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface) if (!priv->elementName.isEmpty()) data->nameToType.insert(priv->elementName, priv); - if (data->interfaces.size() <= interface.typeId) - data->interfaces.resize(interface.typeId + 16); - if (data->lists.size() <= interface.listId) - data->lists.resize(interface.listId + 16); - data->interfaces.setBit(interface.typeId, true); - data->lists.setBit(interface.listId, true); + if (data->interfaces.size() <= type.typeId) + data->interfaces.resize(type.typeId + 16); + if (data->lists.size() <= type.listId) + data->lists.resize(type.listId + 16); + data->interfaces.setBit(type.typeId, true); + data->lists.setBit(type.listId, true); - return type; + return QQmlType(priv); } QString registrationTypeString(QQmlType::RegistrationType typeType) @@ -1701,7 +341,8 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) } // NOTE: caller must hold a QMutexLocker on "data" -bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, const QString &typeName, int majorVersion = -1) +bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, + const char *uri, const QString &typeName, int majorVersion = -1) { if (!typeName.isEmpty()) { if (typeName.at(0).isLower()) { @@ -1736,7 +377,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da versionedUri.uri = nameSpace; versionedUri.majorVersion = majorVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){ - if (QQmlTypeModulePrivate::get(qqtm)->locked){ + if (qqtm->isLocked()){ QString failure(QCoreApplication::translate("qmlRegisterType", "Cannot install %1 '%2' into protected module '%3' version '%4'")); data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion)); @@ -1755,8 +396,7 @@ QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMe QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); QQmlTypeModule *module = data->uriToModule.value(versionedUri); if (!module) { - module = new QQmlTypeModule; - module->d->uri = versionedUri; + module = new QQmlTypeModule(versionedUri.uri, versionedUri.majorVersion); data->uriToModule.insert(versionedUri, module); } return module; @@ -1792,47 +432,47 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) QQmlTypeModule *module = getTypeModule(mod, type->version_maj, data); Q_ASSERT(module); - module->d->add(type); + module->add(type); } } -QQmlType registerType(const QQmlPrivate::RegisterType &type) +QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString elementName = QString::fromUtf8(type.elementName); if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, elementName, type); + QQmlTypePrivate *priv = createQQmlType(data, elementName, type); - addTypeToData(dtype.priv(), data); + addTypeToData(priv, data); if (!type.typeId) - data->idToType.insert(dtype.typeId(), dtype.priv()); + data->idToType.insert(priv->typeId, priv); - return dtype; + return QQmlType(priv); } -QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) +QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, typeName, type); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); - addTypeToData(dtype.priv(), data); + addTypeToData(priv, data); - return dtype; + return QQmlType(priv); } QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); bool fileImport = false; if (*(type.uri) == '\0') @@ -1840,21 +480,20 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, typeName)) return QQmlType(); - QQmlType dtype(data, typeName, type); - - addTypeToData(dtype.priv(), data); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv()); + files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); - return dtype; + return QQmlType(priv); } QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); bool fileImport = false; if (*(type.uri) == '\0') @@ -1862,16 +501,16 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit if (!checkRegistration(QQmlType::CompositeType, data, fileImport?nullptr:type.uri, typeName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, typeName, type); - addTypeToData(dtype.priv(), data); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv()); + files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); - return dtype; + return QQmlType(priv); } -void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { QByteArray name = compilationUnit->rootPropertyCache()->className(); @@ -1894,125 +533,61 @@ void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationU compilationUnit->metaTypeId = ptr_type; compilationUnit->listMetaTypeId = lst_type; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *d = metaTypeData(); - d->qmlLists.insert(lst_type, ptr_type); + QQmlMetaTypeDataPtr data; + data->qmlLists.insert(lst_type, ptr_type); } -void QQmlMetaType::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { int ptr_type = compilationUnit->metaTypeId; int lst_type = compilationUnit->listMetaTypeId; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *d = metaTypeData(); - d->qmlLists.remove(lst_type); + QQmlMetaTypeDataPtr data; + data->qmlLists.remove(lst_type); QMetaType::unregisterType(ptr_type); QMetaType::unregisterType(lst_type); } -int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) +int QQmlMetaType::registerUnitCacheHook( + const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) { if (hookRegistration.version > 0) qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit; return 0; } -/* -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) -{ - if (type == AutoParentRegistration) - return registerAutoParentFunction(*reinterpret_cast<RegisterAutoParent *>(data)); - else if (type == QmlUnitCacheHookRegistration) - return registerQmlUnitCacheHook(*reinterpret_cast<RegisterQmlUnitCacheHook *>(data)); - - QQmlType dtype; - if (type == TypeRegistration) - dtype = registerType(*reinterpret_cast<RegisterType *>(data)); - else if (type == InterfaceRegistration) - dtype = registerInterface(*reinterpret_cast<RegisterInterface *>(data)); - else if (type == SingletonRegistration) - dtype = registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data)); - else if (type == CompositeRegistration) - dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data)); - else if (type == CompositeSingletonRegistration) - dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data)); - else - return -1; - - if (!dtype.isValid()) - return -1; - - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *typeData = metaTypeData(); - typeData->undeletableTypes.insert(dtype); - - return dtype.index(); -} - -void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data) -{ - switch (type) { - case AutoParentRegistration: - unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data)); - break; - case QmlUnitCacheHookRegistration: - QQmlMetaType::removeCachedUnitLookupFunction( - reinterpret_cast<QmlUnitCacheLookupFunction>(data)); - break; - case TypeRegistration: - case InterfaceRegistration: - case SingletonRegistration: - case CompositeRegistration: - case CompositeSingletonRegistration: - qmlUnregisterType(data); - break; - } -} - -//From qqml.h -bool qmlProtectModule(const char *uri, int majVersion) +bool QQmlMetaType::protectModule(const char *uri, int majVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlMetaTypeData::VersionedUri versionedUri; versionedUri.uri = QString::fromUtf8(uri); versionedUri.majorVersion = majVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) { - QQmlTypeModulePrivate::get(qqtm)->locked = true; + qqtm->lock(); return true; } return false; } -//From qqml.h -void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) +void QQmlMetaType::registerModule(const char *uri, int versionMajor, int versionMinor) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); Q_ASSERT(module); - QQmlTypeModulePrivate *p = QQmlTypeModulePrivate::get(module); - p->minMinorVersion = qMin(p->minMinorVersion, versionMinor); - p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor); + module->addMinorVersion(versionMinor); } -//From qqml.h -int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +int QQmlMetaType::typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); if (!module) @@ -2025,34 +600,218 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q return type.index(); } -bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion) +void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) { - const QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + data->undeletableTypes.insert(dtype); +} +static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, + int majorVersion) +{ // Has any type previously been installed to this namespace? QHashedString nameSpace(uri); - for (const QQmlType &type : data->types) + for (const QQmlType &type : data->types) { if (type.module() == nameSpace && type.majorVersion() == majorVersion) return true; + } return false; } -void QQmlMetaType::protectNamespace(const QString &uri) +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; +}; + + +bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath, + const QString &uri, const QString &typeNamespace, int vmaj, + QList<QQmlError> *errors) { - QQmlMetaTypeData *data = metaTypeData(); + QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance); + if (!iface) { + if (errors) { + QQmlError error; + error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement " + "QQmlTypesExtensionInterface").arg(typeNamespace)); + errors->prepend(error); + } + return false; + } + + if (!typeNamespace.isEmpty() && typeNamespace != uri) { + // This is an 'identified' module + // The namespace for type registrations must match the URI for locating the module + if (errors) { + QQmlError error; + error.setDescription( + QStringLiteral("Module namespace '%1' does not match import URI '%2'") + .arg(typeNamespace).arg(uri)); + errors->prepend(error); + } + return false; + } + + QStringList failures; + QQmlMetaTypeDataPtr data; + { + QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); + if (!typeNamespace.isEmpty()) { + // This is an 'identified' module + if (namespaceContainsRegistrations(data, typeNamespace, vmaj)) { + // Other modules have already installed to this namespace + if (errors) { + QQmlError error; + error.setDescription(QStringLiteral("Namespace '%1' has already been used " + "for type registration") + .arg(typeNamespace)); + errors->prepend(error); + } + return false; + } + + data->protectedNamespaces.insert(uri); + } else { + // This is not an identified module - provide a warning + qWarning().nospace() << qPrintable( + QStringLiteral("Module '%1' does not contain a module identifier directive - " + "it cannot be protected from external registrations.").arg(uri)); + } + + if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { + // basepath should point to the directory of the module, not the plugin file itself: + QQmlExtensionPluginPrivate::get(plugin)->baseUrl + = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); + } + + data->typeRegistrationNamespace = typeNamespace; + const QByteArray bytes = uri.toUtf8(); + const char *moduleId = bytes.constData(); + iface->registerTypes(moduleId); + data->typeRegistrationNamespace.clear(); + } + + if (!failures.isEmpty()) { + if (errors) { + for (const QString &failure : qAsConst(failures)) { + QQmlError error; + error.setDescription(failure); + errors->prepend(error); + } + } + return false; + } - data->protectedNamespaces.insert(uri); + return true; } -void QQmlMetaType::setTypeRegistrationNamespace(const QString &uri) +/* + \internal + + Fetches the QQmlType instance registered for \a urlString, creating a + registration for it if it is not already registered, using the associated + \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion + details. + + Errors (if there are any) are placed into \a errors, if it is nonzero. + Otherwise errors are printed as warnings. +*/ +QQmlType QQmlMetaType::typeForUrl(const QString &urlString, + const QHashedStringRef &qualifiedType, + bool isCompositeSingleton, QList<QQmlError> *errors, + int majorVersion, int minorVersion) { - QQmlMetaTypeData *data = metaTypeData(); + // ### unfortunate (costly) conversion + const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString)); - data->typeRegistrationNamespace = uri; + QQmlMetaTypeDataPtr data; + { + QQmlType ret(data->urlToType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } + { + QQmlType ret(data->urlToNonFileImportType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + 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, majorVersion)) { + auto *priv = new QQmlTypePrivate(registrationType); + priv->setName(QString(), typeName); + priv->version_maj = majorVersion; + priv->version_min = minorVersion; + + 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.insertMulti(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('\n')); + errors->prepend(error); + } else { + qWarning("%s", failures.join('\n').toLatin1().constData()); + } + return QQmlType(); } -QMutex *QQmlMetaType::typeRegistrationLock() +QRecursiveMutex *QQmlMetaType::typeRegistrationLock() { return metaTypeDataLock(); } @@ -2062,8 +821,7 @@ QMutex *QQmlMetaType::typeRegistrationLock() */ bool QQmlMetaType::isAnyModule(const QString &uri) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); iter != data->uriToModule.cend(); ++iter) { @@ -2079,14 +837,13 @@ bool QQmlMetaType::isAnyModule(const QString &uri) */ bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlMetaTypeData::VersionedUri versionedUri; versionedUri.uri = uri; versionedUri.majorVersion = majVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) - return QQmlTypeModulePrivate::get(qqtm)->locked; + return qqtm->isLocked(); return false; } @@ -2100,9 +857,7 @@ bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor) { Q_ASSERT(versionMajor >= 0 && versionMinor >= 0); - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // first, check Types QQmlTypeModule *tm = @@ -2115,15 +870,13 @@ bool QQmlMetaType::isModule(const QString &module, int versionMajor, int version QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->uriToModule.value(QQmlMetaTypeData::VersionedUri(uri, majorVersion)); } QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->parentFunctions; } @@ -2144,8 +897,7 @@ bool QQmlMetaType::isQObject(int userType) if (userType == QMetaType::QObjectStar) return true; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); } @@ -2154,8 +906,7 @@ bool QQmlMetaType::isQObject(int userType) */ int QQmlMetaType::listType(int id) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QHash<int, int>::ConstIterator iter = data->qmlLists.constFind(id); if (iter != data->qmlLists.cend()) return *iter; @@ -2166,10 +917,10 @@ int QQmlMetaType::listType(int id) return 0; } +#if QT_DEPRECATED_SINCE(5, 14) int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (auto it = data->metaObjectToType.constFind(mo), end = data->metaObjectToType.constEnd(); it != end && it.key() == mo; ++it) { @@ -2185,16 +936,15 @@ QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePr { if (id < 0) return nullptr; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->types.at(id).attachedPropertiesFunction(engine); } +#endif QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, const QMetaObject *mo) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlType type(data->metaObjectToType.value(mo)); return type.attachedPropertiesFunction(engine); @@ -2259,8 +1009,7 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) if (userType == QMetaType::QObjectStar) return Object; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; if (data->qmlLists.contains(userType)) return List; else if (userType < data->objects.size() && data->objects.testBit(userType)) @@ -2276,17 +1025,20 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) */ bool QQmlMetaType::isInterface(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); } const char *QQmlMetaType::interfaceIId(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - QQmlType type(data->idToType.value(userType)); - lock.unlock(); + + QQmlTypePrivate *typePrivate = nullptr; + { + QQmlMetaTypeDataPtr data; + typePrivate = data->idToType.value(userType); + } + + QQmlType type(typePrivate); if (type.isInterface() && type.typeId() == userType) return type.interfaceIId(); else @@ -2295,8 +1047,7 @@ const char *QQmlMetaType::interfaceIId(int userType) bool QQmlMetaType::isList(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; if (data->qmlLists.contains(userType)) return true; return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); @@ -2319,9 +1070,7 @@ bool QQmlMetaType::isList(int userType) */ void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter) { - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; if (data->stringConverters.contains(type)) return; data->stringConverters.insert(type, converter); @@ -2333,9 +1082,7 @@ void QQmlMetaType::registerCustomStringConverter(int type, StringConverter conve */ QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) { - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; return data->stringConverters.value(type); } @@ -2362,8 +1109,7 @@ QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); while (it != data->nameToType.cend() && it.key() == name) { @@ -2383,9 +1129,7 @@ QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedString */ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - + const QQmlMetaTypeDataPtr data; return QQmlType(data->metaObjectToType.value(metaObject)); } @@ -2397,8 +1141,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); while (it != data->metaObjectToType.cend() && it.key() == metaObject) { @@ -2418,8 +1161,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin */ QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; if (category == TypeIdCategory::MetaType) { QQmlTypePrivate *type = data->idToType.value(typeId); @@ -2442,8 +1184,7 @@ QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports /* = false */) { const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlType type(data->urlToType.value(url)); if (!type.isValid() && includeNonFileImports) @@ -2455,221 +1196,87 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI return QQmlType(); } -QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion) -{ - if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) - return rv; - - if (!metaObject->superClass()) { - QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject); - propertyCaches.insert(metaObject, rv); - return rv; - } - QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion); - QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion); - propertyCaches.insert(metaObject, rv); - return rv; -} - QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject, int minorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // not const: the cache is created on demand return data->propertyCache(metaObject, minorVersion); } -QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion) -{ - Q_ASSERT(type.isValid()); - - if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion)) - return pc; - - QVector<QQmlType> types; - - int maxMinorVersion = 0; - - const QMetaObject *metaObject = type.metaObject(); - - while (metaObject) { - QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); - if (t.isValid()) { - maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); - types << t; - } else { - types << QQmlType(); - } - - metaObject = metaObject->superClass(); - } - - if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) { - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc); - return pc; - } - - QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion); - - bool hasCopied = false; - - for (int ii = 0; ii < types.count(); ++ii) { - QQmlType currentType = types.at(ii); - if (!currentType.isValid()) - continue; - - int rev = currentType.metaObjectRevision(); - int moIndex = types.count() - 1 - ii; - - if (raw->allowedRevisionCache[moIndex] != rev) { - if (!hasCopied) { - raw = raw->copy(); - hasCopied = true; - } - raw->allowedRevisionCache[moIndex] = rev; - } - } - - // Test revision compatibility - the basic rule is: - // * Anything that is excluded, cannot overload something that is not excluded * - - // Signals override: - // * other signals and methods of the same name. - // * properties named on<Signal Name> - // * automatic <property name>Changed notify signals - - // Methods override: - // * other methods of the same name - - // Properties override: - // * other elements of the same name - -#if 0 - bool overloadError = false; - QString overloadName; - - for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); - !overloadError && iter != raw->stringCache.end(); - ++iter) { - - QQmlPropertyData *d = *iter; - if (raw->isAllowedInRevision(d)) - continue; // Not excluded - no problems - - // check that a regular "name" overload isn't happening - QQmlPropertyData *current = d; - while (!overloadError && current) { - current = d->overrideData(current); - if (current && raw->isAllowedInRevision(current)) - overloadError = true; - } - } - - if (overloadError) { - if (hasCopied) raw->release(); - - error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); - return 0; - } -#endif - - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw); - - if (hasCopied) - raw->release(); - - if (minorVersion != maxMinorVersion) - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw); - - return raw; -} - QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // not const: the cache is created on demand return data->propertyCache(type, minorVersion); } -void qmlUnregisterType(int typeIndex) +void QQmlMetaType::unregisterType(int typeIndex) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - { - const QQmlType type = data->types.value(typeIndex); - const QQmlTypePrivate *d = type.priv(); - if (d) { - removeQQmlTypePrivate(data->idToType, d); - removeQQmlTypePrivate(data->nameToType, d); - removeQQmlTypePrivate(data->urlToType, d); - removeQQmlTypePrivate(data->urlToNonFileImportType, d); - removeQQmlTypePrivate(data->metaObjectToType, d); - for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { - QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); - modulePrivate->remove(d); - } - data->types[typeIndex] = QQmlType(); - data->undeletableTypes.remove(type); - } + QQmlMetaTypeDataPtr data; + const QQmlType type = data->types.value(typeIndex); + if (const QQmlTypePrivate *d = type.priv()) { + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + for (auto & module : data->uriToModule) + module->remove(d); + data->clearPropertyCachesForMinorVersion(typeIndex); + data->types[typeIndex] = QQmlType(); + data->undeletableTypes.remove(type); } } void QQmlMetaType::freeUnusedTypesAndCaches() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // in case this is being called during program exit, `data` might be destructed already - if (!data) + if (!data.isValid()) return; - { - 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->refCount == 1) { - deletedAtLeastOneType = true; - - removeQQmlTypePrivate(data->idToType, d); - removeQQmlTypePrivate(data->nameToType, d); - removeQQmlTypePrivate(data->urlToType, d); - removeQQmlTypePrivate(data->urlToNonFileImportType, d); - removeQQmlTypePrivate(data->metaObjectToType, d); - - for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { - QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); - modulePrivate->remove(d); - } - - *it = QQmlType(); - } else { - ++it; - } + 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) { + deletedAtLeastOneType = true; + + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + + for (auto &module : data->uriToModule) + module->remove(d); + + data->clearPropertyCachesForMinorVersion(d->index); + *it = QQmlType(); + } else { + ++it; } - } while (deletedAtLeastOneType); - } - - { - bool deletedAtLeastOneCache; - do { - deletedAtLeastOneCache = false; - QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); - while (it != data->propertyCaches.end()) { - - if ((*it)->count() == 1) { - QQmlPropertyCache *pc = nullptr; - qSwap(pc, *it); - it = data->propertyCaches.erase(it); - pc->release(); - deletedAtLeastOneCache = true; - } else { - ++it; - } + } + } while (deletedAtLeastOneType); + + bool deletedAtLeastOneCache; + do { + deletedAtLeastOneCache = false; + QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); + while (it != data->propertyCaches.end()) { + + if ((*it)->count() == 1) { + QQmlPropertyCache *pc = nullptr; + qSwap(pc, *it); + it = data->propertyCaches.erase(it); + pc->release(); + deletedAtLeastOneCache = true; + } else { + ++it; } - } while (deletedAtLeastOneCache); - } + } + } while (deletedAtLeastOneCache); } /*! @@ -2677,8 +1284,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches() */ QList<QString> QQmlMetaType::qmlTypeNames() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QString> names; names.reserve(data->nameToType.count()); @@ -2697,8 +1303,7 @@ QList<QString> QQmlMetaType::qmlTypeNames() */ QList<QQmlType> QQmlMetaType::qmlTypes() { - QMutexLocker lock(metaTypeDataLock()); - const QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QQmlType> types; for (QQmlTypePrivate *t : data->nameToType) @@ -2712,9 +1317,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes() */ QList<QQmlType> QQmlMetaType::qmlAllTypes() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - + const QQmlMetaTypeDataPtr data; return data->types; } @@ -2723,8 +1326,7 @@ QList<QQmlType> QQmlMetaType::qmlAllTypes() */ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QQmlType> retn; for (const auto t : qAsConst(data->nameToType)) { @@ -2737,13 +1339,12 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { QString error; - if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &error)) { qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; if (status) *status = CachedUnitLookupError::VersionMismatch; @@ -2763,15 +1364,13 @@ const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUr void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit.prepend(handler); } void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit.removeAll(handler); } @@ -2817,4 +1416,38 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) return typeName; } +QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo, + const QMetaObject *baseMetaObject, + QMetaObject *lastMetaObject) +{ + QList<QQmlProxyMetaObject::ProxyData> metaObjects; + mo = mo->d.superdata; + + const QQmlMetaTypeDataPtr data; + + while (mo) { + QQmlTypePrivate *t = data->metaObjectToType.value(mo); + if (t) { + if (t->regType == QQmlType::CppType) { + if (t->extraData.cd->extFunc) { + QMetaObjectBuilder builder; + clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = baseMetaObject; + if (!metaObjects.isEmpty()) + metaObjects.constLast().metaObject->d.superdata = mmo; + else if (lastMetaObject) + lastMetaObject->d.superdata = mmo; + QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; + metaObjects << data; + } + } + } + mo = mo->d.superdata; + } + + return metaObjects; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index abc79e50e2..6c2b0bb2a6 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -51,40 +51,44 @@ // We mean it. // -#include "qqml.h" #include <private/qtqmlglobal_p.h> - -#include <QtCore/qglobal.h> -#include <QtCore/qvariant.h> -#include <QtCore/qbitarray.h> -#include <QtQml/qjsvalue.h> +#include <private/qqmltype_p.h> +#include <private/qqmlproxymetaobject_p.h> QT_BEGIN_NAMESPACE -class QQmlType; -class QQmlEngine; -class QQmlEnginePrivate; -class QQmlCustomParser; -class QQmlTypePrivate; class QQmlTypeModule; -class QHashedString; -class QHashedStringRef; -class QMutex; -class QQmlPropertyCache; -class QQmlCompiledData; - -namespace QV4 { struct String; } +class QRecursiveMutex; +class QQmlError; -void Q_QML_PRIVATE_EXPORT qmlUnregisterType(int type); +namespace QV4 { class ExecutableCompilationUnit; } class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: + 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 registerCompositeType(const QQmlPrivate::RegisterCompositeType &type); + static bool registerPluginTypes(QObject *instance, const QString &basePath, + const QString &uri, const QString &typeNamespace, int vmaj, + QList<QQmlError> *errors); + static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName, + bool isCompositeSingleton, QList<QQmlError> *errors, + int majorVersion = -1, int minorVersion = -1); + + static void unregisterType(int type); + + static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); + static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); - static void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); - static void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + static void registerModule(const char *uri, int versionMajor, int versionMinor); + static bool protectModule(const char *uri, int majVersion); + + static int typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName); + + static void registerUndeletableType(const QQmlType &dtype); static QList<QString> qmlTypeNames(); static QList<QQmlType> qmlTypes(); @@ -117,8 +121,12 @@ public: static QObject *toQObject(const QVariant &, bool *ok = nullptr); static int listType(int); - static int attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *); - static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, int); +#if QT_DEPRECATED_SINCE(5, 14) + static QT_DEPRECATED int attachedPropertiesFuncId(QQmlEnginePrivate *engine, + const QMetaObject *); + static QT_DEPRECATED QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, + int); +#endif static QQmlAttachedPropertiesFunc attachedPropertiesFunc(QQmlEnginePrivate *, const QMetaObject *); @@ -152,228 +160,38 @@ public: static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); - static bool namespaceContainsRegistrations(const QString &, int majorVersion); - - static void protectNamespace(const QString &); - - static void setTypeRegistrationNamespace(const QString &); - - static QMutex *typeRegistrationLock(); + static QRecursiveMutex *typeRegistrationLock(); static QString prettyTypeName(const QObject *object); -}; -struct QQmlMetaTypeData; -class QHashedCStringRef; -class QQmlPropertyCache; -class Q_QML_PRIVATE_EXPORT QQmlType -{ -public: - QQmlType(); - QQmlType(const QQmlType &other); - QQmlType &operator =(const QQmlType &other); - explicit QQmlType(QQmlTypePrivate *priv); - ~QQmlType(); - - bool operator ==(const QQmlType &other) const { - return d == other.d; + template <typename QQmlTypeContainer> + static void removeQQmlTypePrivate(QQmlTypeContainer &container, + const QQmlTypePrivate *reference) + { + for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) { + if (*it == reference) + it = container.erase(it); + else + ++it; + } } - bool isValid() const { return d != nullptr; } - const QQmlTypePrivate *key() const { return d; } + static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent); + static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function); - QByteArray typeName() const; - QString qmlTypeName() const; - QString elementName() const; + static int registerUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration); + static void clearTypeRegistrations(); - QHashedString module() const; - int majorVersion() const; - int minorVersion() const; - - bool availableInVersion(int vmajor, int vminor) const; - bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const; - - QObject *create() const; - void create(QObject **, void **, size_t) const; - - typedef void (*CreateFunc)(void *); - CreateFunc createFunction() const; - QQmlCustomParser *customParser() const; - - bool isCreatable() const; - typedef QObject *(*ExtensionFunc)(QObject *); - ExtensionFunc extensionFunction() const; - bool isExtendedType() const; - QString noCreationReason() const; - - bool isSingleton() const; - bool isInterface() const; - bool isComposite() const; - bool isCompositeSingleton() const; - - int typeId() const; - int qListTypeId() const; - - const QMetaObject *metaObject() const; - const QMetaObject *baseMetaObject() const; - int metaObjectRevision() const; - bool containsRevisionedAttributes() const; - - QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; - const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; - int attachedPropertiesId(QQmlEnginePrivate *engine) const; - - int parserStatusCast() const; - const char *interfaceIId() const; - int propertyValueSourceCast() const; - int propertyValueInterceptorCast() const; - - int index() const; - - class Q_QML_PRIVATE_EXPORT SingletonInstanceInfo - { - public: - SingletonInstanceInfo() - : scriptCallback(nullptr), qobjectCallback(nullptr), instanceMetaObject(nullptr) {} - - QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *); - QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *); - const QMetaObject *instanceMetaObject; - QString typeName; - QUrl url; // used by composite singletons - - void setQObjectApi(QQmlEngine *, QObject *); - QObject *qobjectApi(QQmlEngine *) const; - void setScriptApi(QQmlEngine *, const QJSValue &); - QJSValue scriptApi(QQmlEngine *) const; - - void init(QQmlEngine *); - void destroy(QQmlEngine *); - - QHash<QQmlEngine *, QJSValue> scriptApis; - QHash<QQmlEngine *, QObject *> qobjectApis; - }; - SingletonInstanceInfo *singletonInstanceInfo() const; - - QUrl sourceUrl() const; - - int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; - int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; - int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - - int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - 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, const QStringRef &, const QStringRef &, bool *ok) const; - - QQmlTypePrivate *priv() const { return d; } - static void refHandle(QQmlTypePrivate *priv); - static void derefHandle(QQmlTypePrivate *priv); - static int refCount(QQmlTypePrivate *priv); - - enum RegistrationType { - CppType = 0, - SingletonType = 1, - InterfaceType = 2, - CompositeType = 3, - CompositeSingletonType = 4, - AnyRegistrationType = 255 - }; + static QList<QQmlProxyMetaObject::ProxyData> proxyData(const QMetaObject *mo, + const QMetaObject *baseMetaObject, + QMetaObject *lastMetaObject); -private: - QQmlType superType() const; - QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; - friend class QQmlTypePrivate; - - friend QString registrationTypeString(RegistrationType); - friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &, int); - friend QQmlType registerType(const QQmlPrivate::RegisterType &); - friend QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &); - friend QQmlType registerInterface(const QQmlPrivate::RegisterInterface &); - friend int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &); - friend uint qHash(const QQmlType &t, uint seed); - friend Q_QML_EXPORT void qmlClearTypeRegistrations(); - friend class QQmlMetaType; - - QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterSingletonType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &); - - QQmlTypePrivate *d; + static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd); }; Q_DECLARE_TYPEINFO(QQmlMetaType, Q_MOVABLE_TYPE); - -inline uint qHash(const QQmlType &t, uint seed = 0) { return qHash(reinterpret_cast<quintptr>(t.d), seed); } - - -class QQmlTypeModulePrivate; -class QQmlTypeModule -{ -public: - QString module() const; - int majorVersion() const; - - int minimumMinorVersion() const; - int maximumMinorVersion() const; - - QQmlType type(const QHashedStringRef &, int) const; - QQmlType type(const QV4::String *, int) const; - - void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const; - - QQmlTypeModulePrivate *priv() { return d; } -private: - //Used by register functions and creates the QQmlTypeModule for them - friend QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data); - friend void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data); - friend struct QQmlMetaTypeData; - friend Q_QML_EXPORT void qmlClearTypeRegistrations(); - friend class QQmlTypeModulePrivate; - - QQmlTypeModule(); - ~QQmlTypeModule(); - QQmlTypeModulePrivate *d; -}; - -class QQmlTypeModuleVersion -{ -public: - QQmlTypeModuleVersion(); - QQmlTypeModuleVersion(QQmlTypeModule *, int); - QQmlTypeModuleVersion(const QQmlTypeModuleVersion &); - QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &); - - QQmlTypeModule *module() const; - int minorVersion() const; - - QQmlType type(const QHashedStringRef &) const; - QQmlType type(const QV4::String *) const; - -private: - QQmlTypeModule *m_module; - int m_minor; -}; - -class Q_AUTOTEST_EXPORT QQmlMetaTypeRegistrationFailureRecorder -{ - QStringList _failures; - -public: - QQmlMetaTypeRegistrationFailureRecorder(); - ~QQmlMetaTypeRegistrationFailureRecorder(); - - QStringList failures() const - { return _failures; } -}; - QT_END_NAMESPACE #endif // QQMLMETATYPE_P_H diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp new file mode 100644 index 0000000000..775bc8bdb4 --- /dev/null +++ b/src/qml/qml/qqmlmetatypedata.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlmetatypedata_p.h" + +#include <private/qqmltype_p_p.h> +#include <private/qqmltypemodule_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +QQmlMetaTypeData::QQmlMetaTypeData() +{ +} + +QQmlMetaTypeData::~QQmlMetaTypeData() +{ + for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) + delete *i; + for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); + it != end; ++it) + (*it)->release(); + + // Do this before the attached properties disappear. + types.clear(); + undeletableTypes.clear(); +} + +// This expects a "fresh" QQmlTypePrivate and adopts its reference. +void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) +{ + for (int i = 0; i < types.count(); ++i) { + if (!types.at(i).isValid()) { + types[i] = QQmlType(priv); + priv->index = i; + priv->release(); + return; + } + } + types.append(QQmlType(priv)); + priv->index = types.count() - 1; + priv->release(); +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const +{ + return (index < typePropertyCaches.length()) + ? typePropertyCaches.at(index).value(minorVersion).data() + : nullptr; +} + +void QQmlMetaTypeData::setPropertyCacheForMinorVersion(int index, int minorVersion, + QQmlPropertyCache *cache) +{ + if (index >= typePropertyCaches.length()) + typePropertyCaches.resize(index + 1); + typePropertyCaches[index][minorVersion] = cache; +} + +void QQmlMetaTypeData::clearPropertyCachesForMinorVersion(int index) +{ + if (index < typePropertyCaches.length()) + typePropertyCaches[index].clear(); +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion) +{ + if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) + return rv; + + if (!metaObject->superClass()) { + QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject); + propertyCaches.insert(metaObject, rv); + return rv; + } + QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion); + QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion); + propertyCaches.insert(metaObject, rv); + return rv; +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion) +{ + Q_ASSERT(type.isValid()); + + if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion)) + return pc; + + QVector<QQmlType> types; + + int maxMinorVersion = 0; + + const QMetaObject *metaObject = type.metaObject(); + + while (metaObject) { + QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); + if (t.isValid()) { + maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); + types << t; + } else { + types << QQmlType(); + } + + metaObject = metaObject->superClass(); + } + + if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) { + setPropertyCacheForMinorVersion(type.index(), minorVersion, pc); + return pc; + } + + QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion); + + bool hasCopied = false; + + for (int ii = 0; ii < types.count(); ++ii) { + const QQmlType ¤tType = types.at(ii); + if (!currentType.isValid()) + continue; + + int rev = currentType.metaObjectRevision(); + int moIndex = types.count() - 1 - ii; + + if (raw->allowedRevision(moIndex) != rev) { + if (!hasCopied) { + // TODO: The copy should be mutable, and the original should be const + // Considering this, the setAllowedRevision() below does not violate + // the immutability of already published property caches. + raw = raw->copy(); + hasCopied = true; + } + raw->setAllowedRevision(moIndex, rev); + } + } + + // Test revision compatibility - the basic rule is: + // * Anything that is excluded, cannot overload something that is not excluded * + + // Signals override: + // * other signals and methods of the same name. + // * properties named on<Signal Name> + // * automatic <property name>Changed notify signals + + // Methods override: + // * other methods of the same name + + // Properties override: + // * other elements of the same name + +#if 0 + bool overloadError = false; + QString overloadName; + + for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); + !overloadError && iter != raw->stringCache.end(); + ++iter) { + + QQmlPropertyData *d = *iter; + if (raw->isAllowedInRevision(d)) + continue; // Not excluded - no problems + + // check that a regular "name" overload isn't happening + QQmlPropertyData *current = d; + while (!overloadError && current) { + current = d->overrideData(current); + if (current && raw->isAllowedInRevision(current)) + overloadError = true; + } + } + + if (overloadError) { + if (hasCopied) raw->release(); + + error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + return 0; + } +#endif + + setPropertyCacheForMinorVersion(type.index(), minorVersion, raw); + + if (hasCopied) + raw->release(); + + if (minorVersion != maxMinorVersion) + setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw); + + return raw; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h new file mode 100644 index 0000000000..5239b635ce --- /dev/null +++ b/src/qml/qml/qqmlmetatypedata_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMETATYPEDATA_P_H +#define QQMLMETATYPEDATA_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 <private/qqmltype_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qhashedstring_p.h> + +#include <QtCore/qset.h> +#include <QtCore/qvector.h> +#include <QtCore/qbitarray.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypePrivate; +struct QQmlMetaTypeData +{ + QQmlMetaTypeData(); + ~QQmlMetaTypeData(); + void registerType(QQmlTypePrivate *priv); + QList<QQmlType> types; + QSet<QQmlType> undeletableTypes; + typedef QHash<int, QQmlTypePrivate *> Ids; + Ids idToType; + typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names; + Names nameToType; + typedef QHash<QUrl, 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 + // of them by url, even if it was registered as + // a module via QQmlPrivate::RegisterCompositeType + typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects; + MetaObjects metaObjectToType; + typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; + StringConverters stringConverters; + QVector<QHash<int, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches; + + struct VersionedUri { + VersionedUri() + : majorVersion(0) {} + VersionedUri(const QHashedString &uri, int majorVersion) + : uri(uri), majorVersion(majorVersion) {} + bool operator==(const VersionedUri &other) const { + return other.majorVersion == majorVersion && other.uri == uri; + } + QHashedString uri; + int majorVersion; + }; + typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules; + TypeModules uriToModule; + + QBitArray objects; + QBitArray interfaces; + QBitArray lists; + + QList<QQmlPrivate::AutoParentFunction> parentFunctions; + QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit; + + QSet<QString> protectedNamespaces; + + QString typeRegistrationNamespace; + + QHash<int, int> qmlLists; + + QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches; + + QQmlPropertyCache *propertyCacheForMinorVersion(int index, int minorVersion) const; + void setPropertyCacheForMinorVersion(int index, int minorVersion, QQmlPropertyCache *cache); + void clearPropertyCachesForMinorVersion(int index); + + QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion); + QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); + + void setTypeRegistrationFailures(QStringList *failures) + { + m_typeRegistrationFailures = failures; + } + + void recordTypeRegFailure(const QString &message) + { + if (m_typeRegistrationFailures) + m_typeRegistrationFailures->append(message); + else + qWarning("%s", message.toUtf8().constData()); + } + +private: + QStringList *m_typeRegistrationFailures = nullptr; +}; + +inline uint qHash(const QQmlMetaTypeData::VersionedUri &v) +{ + return v.uri.hash() ^ qHash(v.majorVersion); +} + +QT_END_NAMESPACE + +#endif // QQMLMETATYPEDATA_P_H diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index 0706b8c0cf..1359586b31 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -118,8 +118,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine disconnect(); Q_ASSERT(engine); - if (QObjectPrivate::get(source)->threadData->threadId.load() != - QObjectPrivate::get(engine)->threadData->threadId.load()) { + if (QObjectPrivate::get(source)->threadData->threadId.loadRelaxed() != + QObjectPrivate::get(engine)->threadData->threadId.loadRelaxed()) { QString sourceName; QDebug(&sourceName) << source; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index e45515fbcf..a4270628e8 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,7 +55,9 @@ #include <private/qqmlvaluetypeproxybinding_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmlscriptdata_p.h> #include <private/qjsvalue_p.h> +#include <private/qv4generatorobject_p.h> #include <qtqml_tracepoints_p.h> @@ -73,7 +75,7 @@ struct ActiveOCRestorer }; } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator) : phase(Startup) , compilationUnit(compilationUnit) @@ -100,7 +102,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR } } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compilationUnit(compilationUnit) , propertyCaches(&compilationUnit->propertyCaches) @@ -373,7 +375,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const propertyType = QMetaType::Int; } else { // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. - QVariant value = binding->valueAsString(compilationUnit.data()); + QVariant value = compilationUnit->bindingValueAsString(binding); bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context); Q_ASSERT(ok); Q_UNUSED(ok); @@ -406,7 +408,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const switch (propertyType) { case QMetaType::QVariant: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) == n) { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromInt32(int(n))); @@ -438,11 +440,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const property->writeProperty(_qobject, &nullValue, propertyWriteFlags); } } else { - QString stringValue = binding->valueAsString(compilationUnit.data()); + QString stringValue = compilationUnit->bindingValueAsString(binding); if (property->isVarProperty()) { QV4::ScopedString s(scope, v4->newString(stringValue)); _vmeMetaObject->setVMEProperty(property->coreIndex(), s); } else { + // ### Qt 6: Doing the conversion here where we don't know the eventual target type is rather strange + // and caused for instance QTBUG-78943 QVariant value = QQmlStringConverters::variantFromString(stringValue); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -451,25 +455,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::String: { assertOrNull(binding->evaluatesToString()); - QString value = binding->valueAsString(compilationUnit.data()); + QString value = compilationUnit->bindingValueAsString(binding); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::StringList: { assertOrNull(binding->evaluatesToString()); - QStringList value(binding->valueAsString(compilationUnit.data())); + QStringList value(compilationUnit->bindingValueAsString(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::ByteArray: { assertType(QV4::CompiledData::Binding::Type_String); - QByteArray value(binding->valueAsString(compilationUnit.data()).toUtf8()); + QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8()); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Url: { assertType(QV4::CompiledData::Binding::Type_String); - QString string = binding->valueAsString(compilationUnit.data()); + QString string = compilationUnit->bindingValueAsString(binding); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(string)); @@ -481,7 +485,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::UInt: { assertType(QV4::CompiledData::Binding::Type_Number); - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); uint value = uint(d); property->writeProperty(_qobject, &value, propertyWriteFlags); break; @@ -489,7 +493,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::Int: { assertType(QV4::CompiledData::Binding::Type_Number); - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); int value = int(d); property->writeProperty(_qobject, &value, propertyWriteFlags); break; @@ -497,19 +501,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QMetaType::Float: { assertType(QV4::CompiledData::Binding::Type_Number); - float value = float(binding->valueAsNumber(compilationUnit->constants)); + float value = float(compilationUnit->bindingValueAsNumber(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Double: { assertType(QV4::CompiledData::Binding::Type_Number); - double value = binding->valueAsNumber(compilationUnit->constants); + double value = compilationUnit->bindingValueAsNumber(binding); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Color: { bool ok = false; - uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); + uint colorValue = QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); struct { void *data[4]; } buffer; if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { @@ -520,21 +524,21 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const #if QT_CONFIG(datestring) case QVariant::Date: { bool ok = false; - QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); + QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Time: { bool ok = false; - QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::DateTime: { bool ok = false; - QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QDateTime value = QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); // ### VME compatibility :( { const qint64 date = value.date().toJulianDay(); @@ -548,42 +552,42 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const #endif // datestring case QVariant::Point: { bool ok = false; - QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok).toPoint(); + QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok).toPoint(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::PointF: { bool ok = false; - QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Size: { bool ok = false; - QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok).toSize(); + QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok).toSize(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::SizeF: { bool ok = false; - QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Rect: { bool ok = false; - QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok).toRect(); + QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok).toRect(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::RectF: { bool ok = false; - QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -599,7 +603,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float xp; float yp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -611,7 +615,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float yp; float zy; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -624,7 +628,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float zy; float wp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -637,7 +641,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float yp; float zp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -651,12 +655,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const if (property->propType() == qMetaTypeId<QList<qreal> >()) { assertType(QV4::CompiledData::Binding::Type_Number); QList<qreal> value; - value.append(binding->valueAsNumber(compilationUnit->constants)); + value.append(compilationUnit->bindingValueAsNumber(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<int> >()) { assertType(QV4::CompiledData::Binding::Type_Number); - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); QList<int> value; value.append(int(n)); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -669,7 +673,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { assertType(QV4::CompiledData::Binding::Type_String); - QString urlString = binding->valueAsString(compilationUnit.data()); + QString urlString = compilationUnit->bindingValueAsString(binding); QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(urlString)); QList<QUrl> value; @@ -679,7 +683,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (property->propType() == qMetaTypeId<QList<QString> >()) { assertOrNull(binding->evaluatesToString()); QList<QString> value; - value.append(binding->valueAsString(compilationUnit.data())); + value.append(compilationUnit->bindingValueAsString(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QJSValue>()) { @@ -687,7 +691,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = QJSValue(binding->valueAsBoolean()); } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) == n) { value = QJSValue(int(n)); } else @@ -695,14 +699,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { value = QJSValue::NullValue; } else { - value = QJSValue(binding->valueAsString(compilationUnit.data())); + value = QJSValue(compilationUnit->bindingValueAsString(binding)); } property->writeProperty(_qobject, &value, propertyWriteFlags); break; } // otherwise, try a custom type assignment - QString stringValue = binding->valueAsString(compilationUnit.data()); + QString stringValue = compilationUnit->bindingValueAsString(binding); QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); Q_ASSERT(converter); QVariant value = (*converter)(stringValue); @@ -735,7 +739,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) QQmlListProperty<void> savedList; qSwap(_currentList, savedList); - const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); + const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); @@ -816,7 +820,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); - QV4::CompiledData::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex); + QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType attachedType = tr->type; if (!attachedType.isValid()) { @@ -835,13 +839,14 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper // ### resolve this at compile time if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) { - QQmlScriptString ss(binding->valueAsScriptString(compilationUnit.data()), context->asQQmlContext(), _scopeObject); + QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding), + context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; ss.d.data()->columnNumber = binding->location.column; ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; - ss.d.data()->numberValue = binding->valueAsNumber(compilationUnit->constants); + ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding); QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; @@ -938,7 +943,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper auto bindingTarget = _bindingTarget; auto valueTypeProperty = _valueTypeProperty; - auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) mutable -> bool { if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) return false; @@ -1133,7 +1138,10 @@ void QQmlObjectCreator::setupFunctions() if (!property->isVMEFunction()) continue; - function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); + if (runtimeFunction->isGenerator()) + function = QV4::GeneratorFunction::create(qmlContext, runtimeFunction); + else + function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); _vmeMetaObject->setVmeMethod(property->coreIndex(), function); } } @@ -1184,8 +1192,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo instance = component; ddata = QQmlData::get(instance, /*create*/true); } else { - QV4::CompiledData::ResolvedTypeReference *typeRef - = resolvedType(obj->inheritedTypeNameIndex); + QV4::ResolvedTypeReference *typeRef = resolvedType(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType type = typeRef->type; @@ -1445,8 +1452,12 @@ void QQmlObjectCreator::clear() return; Q_ASSERT(phase != Startup); - while (!sharedState->allCreatedObjects.isEmpty()) - delete sharedState->allCreatedObjects.pop(); + while (!sharedState->allCreatedObjects.isEmpty()) { + auto object = sharedState->allCreatedObjects.pop(); + if (engine->objectOwnership(object) != QQmlEngine::CppOwnership) { + delete object; + } + } while (sharedState->componentAttached) { QQmlComponentAttached *a = sharedState->componentAttached; diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 5aca60e2f0..ecdbcc56dd 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -53,17 +53,16 @@ #include <private/qqmlimport_p.h> #include <private/qqmltypenamecache_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmltypecompiler_p.h> #include <private/qfinitestack_p.h> #include <private/qrecursionwatcher_p.h> #include <private/qqmlprofiler_p.h> +#include <private/qv4qmlcontext_p.h> #include <qpointer.h> QT_BEGIN_NAMESPACE class QQmlAbstractBinding; -struct QQmlTypeCompiler; class QQmlInstantiationInterrupt; class QQmlIncubatorPrivate; @@ -85,7 +84,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); + QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); ~QQmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr); @@ -94,17 +93,17 @@ public: QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); void clear(); - QQmlComponentAttached **componentAttachment() const { return &sharedState->componentAttached; } + QQmlComponentAttached **componentAttachment() { return &sharedState->componentAttached; } - QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() const { return &sharedState->finalizeCallbacks; } + QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() { return &sharedState->finalizeCallbacks; } QList<QQmlError> errors; QQmlContextData *parentContextData() const { return parentContext.contextData(); } - QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; } + QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; } private: - QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); void init(QQmlContextData *parentContext); @@ -125,7 +124,7 @@ private: inline QV4::QmlContext *currentQmlContext(); Q_NEVER_INLINE void createQmlContext(); - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return compilationUnit->resolvedType(id); } @@ -141,7 +140,7 @@ private: QQmlEngine *engine; QV4::ExecutionEngine *v4; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; diff --git a/src/qml/qml/qqmlobjectorgadget.cpp b/src/qml/qml/qqmlobjectorgadget.cpp new file mode 100644 index 0000000000..1d4916d7d1 --- /dev/null +++ b/src/qml/qml/qqmlobjectorgadget.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectorgadget_p.h" + +QT_BEGIN_NAMESPACE + +void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const +{ + if (ptr.isNull()) { + const QMetaObject *metaObject = _m.asT2(); + metaObject->d.static_metacall(nullptr, type, index, argv); + } + else if (ptr.isT1()) { + QMetaObject::metacall(ptr.asT1(), type, index, argv); + } + else { + const QMetaObject *metaObject = _m.asT1()->metaObject(); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); + metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlobjectorgadget_p.h b/src/qml/qml/qqmlobjectorgadget_p.h new file mode 100644 index 0000000000..c5f5f58a3a --- /dev/null +++ b/src/qml/qml/qqmlobjectorgadget_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLOBJECTORGADGET_P_H +#define QQMLOBJECTORGADGET_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 <private/qqmlmetaobject_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlObjectOrGadget: public QQmlMetaObject +{ +public: + QQmlObjectOrGadget(QObject *obj) + : QQmlMetaObject(obj), + ptr(obj) + {} + QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget) + : QQmlMetaObject(propertyCache) + , ptr(gadget) + {} + + void metacall(QMetaObject::Call type, int index, void **argv) const; + +private: + QBiPointer<QObject, void> ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} +}; + +QT_END_NAMESPACE + +#endif // QQMLOBJECTORGADGET_P_H diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index fc798a2c23..fe0946c6de 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -41,7 +41,6 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmldata_p.h> #include <private/qmetaobjectbuilder_p.h> -#include <private/qv8engine_p.h> #include <qqmlengine.h> #include <qdebug.h> diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index 8a45de9f76..6d7a2569bc 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -51,11 +51,14 @@ // We mean it. // +#include <functional> + #include <QtQml/qtqmlglobal.h> #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> #include <QtCore/qurl.h> +#include <QtCore/qpointer.h> QT_BEGIN_NAMESPACE @@ -276,6 +279,7 @@ namespace QQmlPrivate const QMetaObject *instanceMetaObject; // new in version 1 int typeId; // new in version 2 int revision; // new in version 2 + std::function<QObject*(QQmlEngine *, QJSEngine *)> generalizedQobjectApi; // new in version 3 // If this is extended ensure "version" is bumped!!! }; @@ -319,6 +323,13 @@ namespace QQmlPrivate int Q_QML_EXPORT qmlregister(RegistrationType, void *); void Q_QML_EXPORT qmlunregister(RegistrationType, quintptr); + struct Q_QML_EXPORT RegisterSingletonFunctor + { + QObject *operator()(QQmlEngine *, QJSEngine *); + + QPointer<QObject> m_object; + bool alreadyCalled = false; + }; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index a394ed1ad9..f5aae8f462 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1222,10 +1222,18 @@ bool QQmlPropertyPrivate::write(QObject *object, if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { return property.writeProperty(object, const_cast<void *>(value.constData()), flags); } else if (property.isQObject()) { - QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType); + QVariant val = value; + int varType = variantType; + if (variantType == QMetaType::Nullptr) { + // This reflects the fact that you can assign a nullptr to a QObject pointer + // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject + varType = QMetaType::QObjectStar; + val = QVariant(QMetaType::QObjectStar, nullptr); + } + QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType); if (valMo.isNull()) return false; - QObject *o = *static_cast<QObject *const *>(value.constData()); + QObject *o = *static_cast<QObject *const *>(val.constData()); QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); if (o) @@ -1371,8 +1379,9 @@ bool QQmlPropertyPrivate::write(QObject *object, } } if (!ok) { - // the only other option is that they are assigning a single value + // the only other options are that they are assigning a single value // to a sequence type property (eg, an int to a QList<int> property). + // or that we encountered an interface type // Note that we've already handled single-value assignment to QList<QUrl> properties. if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) { QList<int> list; @@ -1403,6 +1412,15 @@ bool QQmlPropertyPrivate::write(QObject *object, } } + if (!ok && QQmlMetaType::isInterface(propertyType)) { + auto valueAsQObject = qvariant_cast<QObject *>(value); + if (valueAsQObject && valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyType))) { + // this case can occur when object has an interface type + // and the variant contains a type implementing the interface + return property.writeProperty(object, const_cast<void *>(value.constData()), flags); + } + } + if (ok) { return property.writeProperty(object, const_cast<void *>(v.constData()), flags); } else { diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 544eab4c7f..285c34d7fa 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -56,14 +56,17 @@ #include <private/qobject_p.h> #include <private/qtqmlglobal_p.h> -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qqmlcontext_p.h> #include <private/qqmlboundsignalexpressionpointer_p.h> +#include <private/qqmlpropertydata_p.h> QT_BEGIN_NAMESPACE class QQmlContext; class QQmlEnginePrivate; class QQmlJavaScriptExpression; +class QQmlMetaObject; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 73bfd7bbaa..69957ab282 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -42,10 +42,10 @@ #include <private/qqmlengine_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlvmemetaobject_p.h> -#include <private/qv8engine_p.h> #include <private/qmetaobject_p.h> #include <private/qmetaobjectbuilder_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> #include <private/qv4value_p.h> @@ -65,21 +65,6 @@ QT_BEGIN_NAMESPACE #define Q_INT16_MAX 32767 -class QQmlPropertyCacheMethodArguments -{ -public: - QQmlPropertyCacheMethodArguments *next; - - //for signal handler rewrites - QString *signalParameterStringForJS; - int parameterError:1; - int argumentsValid:1; - - QList<QByteArray> *names; - - int arguments[1]; -}; - // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick // to load static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p) @@ -113,8 +98,6 @@ static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags) flags.type = QQmlPropertyData::Flags::QmlBindingType; } else if (propType == qMetaTypeId<QJSValue>()) { flags.type = QQmlPropertyData::Flags::QJSValueType; - } else if (propType == qMetaTypeId<QQmlV4Handle>()) { - flags.type = QQmlPropertyData::Flags::V4HandleType; } else { QQmlMetaType::TypeCategory cat = QQmlMetaType::typeCategory(propType); @@ -141,24 +124,27 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p) return flags; } -void QQmlPropertyData::lazyLoad(const QMetaProperty &p) +static void populate(QQmlPropertyData *data, const QMetaProperty &p) { - setCoreIndex(p.propertyIndex()); - setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); Q_ASSERT(p.revision() <= Q_INT16_MAX); - setRevision(p.revision()); - - setFlags(fastFlagsForProperty(p)); + data->setCoreIndex(p.propertyIndex()); + data->setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); + data->setFlags(fastFlagsForProperty(p)); + data->setRevision(p.revision()); +} +void QQmlPropertyData::lazyLoad(const QMetaProperty &p) +{ + populate(this, p); int type = static_cast<int>(p.type()); if (type == QMetaType::QObjectStar) { setPropType(type); - _flags.type = Flags::QObjectDerivedType; + m_flags.type = Flags::QObjectDerivedType; } else if (type == QMetaType::QVariant) { setPropType(type); - _flags.type = Flags::QVariantType; + m_flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { - _flags.notFullyResolved = true; + m_flags.notFullyResolved = true; } else { setPropType(type); } @@ -166,13 +152,9 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) void QQmlPropertyData::load(const QMetaProperty &p) { + populate(this, p); setPropType(p.userType()); - setCoreIndex(p.propertyIndex()); - setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); - setFlags(fastFlagsForProperty(p)); - flagsForPropertyType(propType(), _flags); - Q_ASSERT(p.revision() <= Q_INT16_MAX); - setRevision(p.revision()); + flagsForPropertyType(propType(), m_flags); } void QQmlPropertyData::load(const QMetaMethod &m) @@ -182,23 +164,23 @@ void QQmlPropertyData::load(const QMetaMethod &m) setPropType(m.returnType()); - _flags.type = Flags::FunctionType; - if (m.methodType() == QMetaMethod::Signal) - _flags.isSignal = true; - else if (m.methodType() == QMetaMethod::Constructor) { - _flags.isConstructor = true; + m_flags.type = Flags::FunctionType; + if (m.methodType() == QMetaMethod::Signal) { + m_flags.isSignal = true; + } else if (m.methodType() == QMetaMethod::Constructor) { + m_flags.isConstructor = true; setPropType(QMetaType::QObjectStar); } - if (m.parameterCount()) { - _flags.hasArguments = true; - if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - _flags.isV4Function = true; - } + const int paramCount = m.parameterCount(); + if (paramCount) { + m_flags.hasArguments = true; + if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) + m_flags.isV4Function = true; } if (m.attributes() & QMetaMethod::Cloned) - _flags.isCloned = true; + m_flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); setRevision(m.revision()); @@ -206,37 +188,14 @@ void QQmlPropertyData::load(const QMetaMethod &m) void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { - setCoreIndex(m.methodIndex()); - setPropType(QMetaType::Void); - setArguments(nullptr); - _flags.type = Flags::FunctionType; - if (m.methodType() == QMetaMethod::Signal) - _flags.isSignal = true; - else if (m.methodType() == QMetaMethod::Constructor) { - _flags.isConstructor = true; - setPropType(QMetaType::QObjectStar); - } + load(m); const char *returnType = m.typeName(); if (!returnType) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - _flags.notFullyResolved = true; - } - - const int paramCount = m.parameterCount(); - if (paramCount) { - _flags.hasArguments = true; - if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - _flags.isV4Function = true; - } + m_flags.notFullyResolved = true; } - - if (m.attributes() & QMetaMethod::Cloned) - _flags.isCloned = true; - - Q_ASSERT(m.revision() <= Q_INT16_MAX); - setRevision(m.revision()); } /*! @@ -361,7 +320,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag data.setArguments(nullptr); QQmlPropertyData handler = data; - handler._flags.isSignalHandler = true; + handler.m_flags.isSignalHandler = true; if (types) { int argumentCount = *types; @@ -389,17 +348,18 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag } void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags, - int coreIndex, const QList<QByteArray> &names) + int coreIndex, int returnType, const QList<QByteArray> &names, + const QVector<int> ¶meterTypes) { int argumentCount = names.count(); QQmlPropertyData data; - data.setPropType(QMetaType::QVariant); + data.setPropType(returnType); data.setCoreIndex(coreIndex); QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); for (int ii = 0; ii < argumentCount; ++ii) - args->arguments[ii + 1] = QMetaType::QVariant; + args->arguments[ii + 1] = parameterTypes.at(ii); args->argumentsValid = true; data.setArguments(args); @@ -559,7 +519,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, data->setFlags(methodFlags); data->lazyLoad(m); - data->_flags.isDirect = !dynamicMetaObject; + data->m_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->setMetaObjectOffset(allowedRevisionCache.count() - 1); @@ -567,7 +527,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (data->isSignal()) { sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart]; *sigdata = *data; - sigdata->_flags.isSignalHandler = true; + sigdata->m_flags.isSignalHandler = true; } QQmlPropertyData *old = nullptr; @@ -609,7 +569,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (old) { // We only overload methods in the same class, exactly like C++ if (old->isFunction() && old->coreIndex() >= methodOffset) - data->_flags.isOverload = true; + data->m_flags.isOverload = true; data->markAsOverrideOf(old); } @@ -640,7 +600,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, data->lazyLoad(p); data->setTypeMinorVersion(typeMinorVersion); - data->_flags.isDirect = !dynamicMetaObject; + data->m_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->setMetaObjectOffset(allowedRevisionCache.count() - 1); @@ -666,7 +626,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, } if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept - data->_flags.isDirect = false; + data->m_flags.isDirect = false; else data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset); if (old) @@ -677,7 +637,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, void QQmlPropertyCache::resolve(QQmlPropertyData *data) const { Q_ASSERT(data->notFullyResolved()); - data->_flags.notFullyResolved = false; + data->m_flags.notFullyResolved = false; const QMetaObject *mo = firstCppMetaObject(); if (data->isFunction()) { @@ -712,7 +672,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); } } - flagsForPropertyType(data->propType(), data->_flags); + flagsForPropertyType(data->propType(), data->m_flags); } } @@ -889,52 +849,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) setOverrideIndexIsProperty(!predecessor->isFunction()); setOverrideIndex(predecessor->coreIndex()); - predecessor->_flags.isOverridden = true; -} - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &staticQtMetaObject; } -}; - -static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, - const QByteArray &name) -{ - for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { - QMetaEnum m = resolvedMetaObject->enumerator(i); - if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) - return true; - } - return false; -} - -static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) -{ - QByteArray scope; - QByteArray name; - int scopeIdx = scopedName.lastIndexOf("::"); - if (scopeIdx != -1) { - scope = scopedName.left(scopeIdx); - name = scopedName.mid(scopeIdx + 2); - } else { - name = scopedName; - } - - if (scope == "Qt") - return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name); - - if (isNamedEnumeratorInScope(metaObj, scope, name)) - return true; - - if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { - for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { - if (isNamedEnumeratorInScope(*related, scope, name)) - return true; - } - } - - return false; + predecessor->m_flags.isOverridden = true; } QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) @@ -954,7 +869,7 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString) { bool unnamedParameter = false; - const QSet<QString> &illegalNames = engine->v8Engine->illegalNames(); + const QSet<QString> &illegalNames = engine->illegalNames(); QString parameters; for (int i = 0; i < parameterNameList.count(); ++i) { @@ -1517,262 +1432,4 @@ QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const return QList<QByteArray>(); } -// 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()); - - struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) { - return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); - } }; - - const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2(); - if (tom == &QObject::staticMetaObject) return true; - - if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache - QQmlPropertyCache *fromp = from._m.asT1(); - QQmlPropertyCache *top = to._m.asT1(); - - while (fromp) { - if (fromp == top) return true; - fromp = fromp->parent(); - } - } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject - QQmlPropertyCache *fromp = from._m.asT1(); - - while (fromp) { - const QMetaObject *fromm = fromp->metaObject(); - if (fromm && I::equal(fromm, tom)) return true; - fromp = fromp->parent(); - } - } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache - const QMetaObject *fromm = from._m.asT2(); - - if (!tom) return false; - - while (fromm) { - if (I::equal(fromm, tom)) return true; - fromm = fromm->superClass(); - } - } else { // QMetaObject -> QMetaObject - const QMetaObject *fromm = from._m.asT2(); - - while (fromm) { - if (I::equal(fromm, tom)) return true; - fromm = fromm->superClass(); - } - } - - return false; -} - -void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) -{ - int offset; - - switch (type) { - case QMetaObject::ReadProperty: - case QMetaObject::WriteProperty: - case QMetaObject::ResetProperty: - case QMetaObject::QueryPropertyDesignable: - case QMetaObject::QueryPropertyEditable: - case QMetaObject::QueryPropertyScriptable: - case QMetaObject::QueryPropertyStored: - case QMetaObject::QueryPropertyUser: - offset = (*metaObject)->propertyOffset(); - while (*index < offset) { - *metaObject = (*metaObject)->superClass(); - offset = (*metaObject)->propertyOffset(); - } - break; - case QMetaObject::InvokeMetaMethod: - offset = (*metaObject)->methodOffset(); - while (*index < offset) { - *metaObject = (*metaObject)->superClass(); - offset = (*metaObject)->methodOffset(); - } - break; - default: - offset = 0; - Q_UNIMPLEMENTED(); - offset = INT_MAX; - } - - *index -= offset; -} - -QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const -{ - if (_m.isNull()) return nullptr; - if (_m.isT1()) return _m.asT1(); - else return e->cache(_m.asT2()); -} - -int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const -{ - Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); - - int type = data.propType(); - - const char *propTypeName = nullptr; - - if (type == QMetaType::UnknownType) { - // Find the return type name from the method info - QMetaMethod m; - - if (_m.isT1()) { - QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); - - while (data.coreIndex() < c->methodIndexCacheStart) - c = c->_parent; - - const QMetaObject *metaObject = c->createMetaObject(); - Q_ASSERT(metaObject); - m = metaObject->method(data.coreIndex()); - } else { - m = _m.asT2()->method(data.coreIndex()); - } - - type = m.returnType(); - propTypeName = m.typeName(); - } - - if (QMetaType::sizeOf(type) <= int(sizeof(int))) { - if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) - return QMetaType::Int; - - if (isNamedEnumerator(metaObject(), propTypeName)) - return QMetaType::Int; - - if (type == QMetaType::UnknownType) { - if (unknownTypeError) - *unknownTypeError = propTypeName; - } - } // else we know that it's a known type, as sizeOf(UnknownType) == 0 - - return type; -} - -int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const -{ - Q_ASSERT(!_m.isNull() && index >= 0); - - if (_m.isT1()) { - typedef QQmlPropertyCacheMethodArguments A; - - QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); - - while (index < c->methodIndexCacheStart) - c = c->_parent; - - QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); - - if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) - return static_cast<A *>(rv->arguments())->arguments; - - const QMetaObject *metaObject = c->createMetaObject(); - Q_ASSERT(metaObject); - QMetaMethod m = metaObject->method(index); - - int argc = m.parameterCount(); - if (!rv->arguments()) { - A *args = c->createArgumentsObject(argc, m.parameterNames()); - rv->setArguments(args); - } - A *args = static_cast<A *>(rv->arguments()); - - QList<QByteArray> argTypeNames; // Only loaded if needed - - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - - if (QMetaType::sizeOf(type) > int(sizeof(int))) { - // Cannot be passed as int - // We know that it's a known type, as sizeOf(UnknownType) == 0 - } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { - type = QMetaType::Int; - } else { - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { - type = QMetaType::Int; - } else if (type == QMetaType::UnknownType){ - if (unknownTypeError) - *unknownTypeError = argTypeNames.at(ii); - return nullptr; - } - - } - args->arguments[ii + 1] = type; - } - args->argumentsValid = true; - return static_cast<A *>(rv->arguments())->arguments; - - } else { - QMetaMethod m = _m.asT2()->method(index); - return methodParameterTypes(m, argStorage, unknownTypeError); - - } -} - -int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const -{ - Q_ASSERT(argStorage); - - int argc = m.parameterCount(); - argStorage->resize(argc + 1); - argStorage->operator[](0) = argc; - QList<QByteArray> argTypeNames; // Only loaded if needed - - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - if (QMetaType::sizeOf(type) > int(sizeof(int))) { - // Cannot be passed as int - // We know that it's a known type, as sizeOf(UnknownType) == 0 - } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { - type = QMetaType::Int; - } else { - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { - type = QMetaType::Int; - } else if (type == QMetaType::UnknownType) { - if (unknownTypeError) - *unknownTypeError = argTypeNames.at(ii); - return nullptr; - } - } - argStorage->operator[](ii + 1) = type; - } - - return argStorage->data(); -} - -void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const -{ - if (ptr.isNull()) { - const QMetaObject *metaObject = _m.asT2(); - metaObject->d.static_metacall(nullptr, type, index, argv); - } - else if (ptr.isT1()) { - QMetaObject::metacall(ptr.asT1(), type, index, argv); - } - else { - const QMetaObject *metaObject = _m.asT1()->metaObject(); - QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); - metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv); - } -} - -int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, - QByteArray *unknownTypeError) const -{ - QMetaMethod m = _m.asT2()->constructor(index); - return methodParameterTypes(m, dummy, unknownTypeError); -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index c3c818eb77..bfd78eef88 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -57,339 +57,25 @@ #include "qqmlnotifier_p.h" #include <private/qqmlpropertyindex_p.h> -#include <private/qhashedstring_p.h> +#include <private/qlinkedstringhash_p.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> #include <private/qv4value_p.h> +#include <private/qqmlpropertydata_p.h> +#include <private/qqmlenumdata_p.h> +#include <private/qqmlenumvalue_p.h> #include <limits> QT_BEGIN_NAMESPACE class QCryptographicHash; -class QMetaProperty; -class QQmlEngine; class QJSEngine; -class QQmlPropertyData; class QMetaObjectBuilder; -class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; -template <typename T> class QQmlPropertyCacheCreator; -template <typename T> class QQmlPropertyCacheAliasCreator; - -// We have this somewhat awful split between RawData and Data so that RawData can be -// used in unions. In normal code, you should always use Data which initializes RawData -// to an invalid state on construction. -// ### We should be able to remove this split nowadays -class QQmlPropertyRawData -{ -public: - typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; - - struct Flags { - enum Types { - 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, // Property type is a QQmlBinding* - QJSValueType = 6, // Property type is a QScriptValue - V4HandleType = 7, // Property type is a QQmlV4Handle - VarPropertyType = 8, // Property type is a "var" property of VMEMO - QVariantType = 9 // Property is a QVariant - }; - - // 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. - // (Note: this padding is done here, because certain compilers have surprising behavior - // when an enum is declared in-between two bit fields.) - enum { BitsLeftInFlags = 10 }; - unsigned _otherBits : BitsLeftInFlags; // align to 32 bits - - // Can apply to all properties, except IsFunction - unsigned isConstant : 1; // Has CONST flag - unsigned isWritable : 1; // Has WRITE function - unsigned isResettable : 1; // Has RESET function - unsigned isAlias : 1; // Is a QML alias to another property - unsigned isFinal : 1; // Has FINAL flag - unsigned isOverridden : 1; // Is overridden by a extension property - unsigned isDirect : 1; // Exists on a C++ QMetaObject - - unsigned type : 4; // stores an entry of Types - - // Apply only to IsFunctions - unsigned isVMEFunction : 1; // Function was added by QML - unsigned hasArguments : 1; // Function takes arguments - unsigned isSignal : 1; // Function is a signal - unsigned isVMESignal : 1; // Signal was added by QML - unsigned isV4Function : 1; // Function takes QQmlV4Function* args - unsigned isSignalHandler : 1; // Function is a signal handler - unsigned isOverload : 1; // Function is an overload of another function - unsigned isCloned : 1; // The function was marked as cloned - unsigned isConstructor : 1; // The function was marked is a constructor - - // Internal QQmlPropertyCache flags - unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved - unsigned overrideIndexIsProperty: 1; - - inline Flags(); - inline bool operator==(const Flags &other) const; - inline void copyPropertyTypeFlags(Flags from); - }; - - Flags flags() const { return _flags; } - void setFlags(Flags f) - { - unsigned otherBits = _flags._otherBits; - _flags = f; - _flags._otherBits = otherBits; - } - - bool isValid() const { return coreIndex() != -1; } - - bool isConstant() const { return _flags.isConstant; } - bool isWritable() const { return _flags.isWritable; } - void setWritable(bool onoff) { _flags.isWritable = onoff; } - bool isResettable() const { return _flags.isResettable; } - bool isAlias() const { return _flags.isAlias; } - bool isFinal() const { return _flags.isFinal; } - bool isOverridden() const { return _flags.isOverridden; } - bool isDirect() const { return _flags.isDirect; } - bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } - bool isFunction() const { return _flags.type == Flags::FunctionType; } - bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } - bool isEnum() const { return _flags.type == Flags::EnumType; } - bool isQList() const { return _flags.type == Flags::QListType; } - bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; } - bool isQJSValue() const { return _flags.type == Flags::QJSValueType; } - bool isV4Handle() const { return _flags.type == Flags::V4HandleType; } - bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; } - bool isQVariant() const { return _flags.type == Flags::QVariantType; } - bool isVMEFunction() const { return _flags.isVMEFunction; } - bool hasArguments() const { return _flags.hasArguments; } - bool isSignal() const { return _flags.isSignal; } - bool isVMESignal() const { return _flags.isVMESignal; } - bool isV4Function() const { return _flags.isV4Function; } - bool isSignalHandler() const { return _flags.isSignalHandler; } - bool isOverload() const { return _flags.isOverload; } - void setOverload(bool onoff) { _flags.isOverload = onoff; } - bool isCloned() const { return _flags.isCloned; } - bool isConstructor() const { return _flags.isConstructor; } - - bool hasOverride() const { return overrideIndex() >= 0; } - bool hasRevision() const { return revision() != 0; } - - bool isFullyResolved() const { return !_flags.notFullyResolved; } - - int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } - void setPropType(int pt) - { - Q_ASSERT(pt >= 0); - Q_ASSERT(pt <= std::numeric_limits<qint16>::max()); - _propType = quint16(pt); - } - - int notifyIndex() const { return _notifyIndex; } - void setNotifyIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _notifyIndex = qint16(idx); - } - - bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } - void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } - - int overrideIndex() const { return _overrideIndex; } - void setOverrideIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _overrideIndex = qint16(idx); - } - - int coreIndex() const { return _coreIndex; } - void setCoreIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _coreIndex = qint16(idx); - } - - quint8 revision() const { return _revision; } - void setRevision(quint8 rev) - { - Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); - _revision = quint8(rev); - } - - /* If a property is a C++ type, then we store the minor - * version of this type. - * This is required to resolve property or signal revisions - * if this property is used as a grouped property. - * - * Test.qml - * property TextEdit someTextEdit: TextEdit {} - * - * Test { - * someTextEdit.preeditText: "test" //revision 7 - * someTextEdit.onEditingFinished: console.log("test") //revision 6 - * } - * - * To determine if these properties with revisions are available we need - * the minor version of TextEdit as imported in Test.qml. - * - */ - - quint8 typeMinorVersion() const { return _typeMinorVersion; } - void setTypeMinorVersion(quint8 rev) - { - Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); - _typeMinorVersion = quint8(rev); - } - - QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } - void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } - - int metaObjectOffset() const { return _metaObjectOffset; } - void setMetaObjectOffset(int off) - { - Q_ASSERT(off >= std::numeric_limits<qint16>::min()); - Q_ASSERT(off <= std::numeric_limits<qint16>::max()); - _metaObjectOffset = qint16(off); - } - - StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; } - void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) - { - if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { - _flags._otherBits = relativePropertyIndex; - _staticMetaCallFunction = f; - } - } - quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; } - -private: - Flags _flags; - qint16 _coreIndex = 0; - quint16 _propType = 0; - - // The notify index is in the range returned by QObjectPrivate::signalIndex(). - // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex = 0; - qint16 _overrideIndex = 0; - - quint8 _revision = 0; - quint8 _typeMinorVersion = 0; - qint16 _metaObjectOffset = 0; - - QQmlPropertyCacheMethodArguments *_arguments = nullptr; - StaticMetaCallFunction _staticMetaCallFunction = nullptr; - - friend class QQmlPropertyData; - friend class QQmlPropertyCache; -}; - -#if QT_POINTER_SIZE == 4 -Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24); -#else // QT_POINTER_SIZE == 8 -Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32); -#endif - -class QQmlPropertyData : public QQmlPropertyRawData -{ -public: - enum WriteFlag { - BypassInterceptor = 0x01, - DontRemoveBinding = 0x02, - RemoveBindingOnAliasWrite = 0x04 - }; - Q_DECLARE_FLAGS(WriteFlags, WriteFlag) - - inline QQmlPropertyData(); - inline QQmlPropertyData(const QQmlPropertyRawData &); - - inline bool operator==(const QQmlPropertyRawData &); - - static Flags flagsForProperty(const QMetaProperty &); - void load(const QMetaProperty &); - void load(const QMetaMethod &); - QString name(QObject *) const; - QString name(const QMetaObject *) const; - - void markAsOverrideOf(QQmlPropertyData *predecessor); - - inline void readProperty(QObject *target, void *property) const - { - void *args[] = { property, nullptr }; - readPropertyWithArgs(target, args); - } - - inline void readPropertyWithArgs(QObject *target, void *args[]) const - { - if (hasStaticMetaCallFunction()) - staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); - else if (isDirect()) - target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); - else - QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); - } - - bool writeProperty(QObject *target, void *value, WriteFlags flags) const - { - int status = -1; - void *argv[] = { value, nullptr, &status, &flags }; - if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction()) - staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); - else if (flags.testFlag(BypassInterceptor) && isDirect()) - target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); - else - QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); - return true; - } - - static Flags defaultSignalFlags() - { - Flags f; - f.isSignal = true; - f.type = Flags::FunctionType; - f.isVMESignal = true; - return f; - } - - static Flags defaultSlotFlags() - { - Flags f; - f.type = Flags::FunctionType; - f.isVMEFunction = true; - return f; - } - -private: - friend class QQmlPropertyCache; - void lazyLoad(const QMetaProperty &); - void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return _flags.notFullyResolved; } -}; - -struct QQmlEnumValue -{ - QQmlEnumValue() {} - QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {} - QString namedValue; - int value = -1; -}; - -struct QQmlEnumData -{ - QString name; - QVector<QQmlEnumValue> values; -}; - class QQmlPropertyCacheMethodArguments; + class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount { public: @@ -399,28 +85,26 @@ public: void update(const QMetaObject *); void invalidate(const QMetaObject *); - // Used by qmlpuppet. Remove as soon Creator requires Qt 5.5. - void invalidate(void *, const QMetaObject *mo) { invalidate(mo); } QQmlPropertyCache *copy(); QQmlPropertyCache *copyAndAppend(const QMetaObject *, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndReserve(int propertyCount, int methodCount, int signalCount, int enumCount); - void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex, + void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int propType, int revision, int notifyIndex); - void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex, + void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex, const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>()); - void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, - const QList<QByteArray> &names = QList<QByteArray>()); + void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int returnType, + const QList<QByteArray> &names, const QVector<int> ¶meterTypes); void appendEnum(const QString &, const QVector<QQmlEnumValue> &); const QMetaObject *metaObject() const; @@ -488,6 +172,10 @@ public: static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); QByteArray checksum(bool *ok); + + int allowedRevision(int index) const { return allowedRevisionCache[index]; } + void setAllowedRevision(int index, int allowed) { allowedRevisionCache[index] = allowed; } + private: friend class QQmlEnginePrivate; friend class QQmlCompiler; @@ -495,19 +183,18 @@ private: template <typename T> friend class QQmlPropertyCacheAliasCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; - friend struct QQmlMetaTypeData; inline QQmlPropertyCache *copy(int reserve); void append(const QMetaObject *, int typeMinorVersion, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names); typedef QVector<QQmlPropertyData> IndexCache; - typedef QStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; + typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; typedef QVector<int> AllowedRevisionCache; QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const; @@ -556,172 +243,6 @@ private: QByteArray _checksum; }; -typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr; - -// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. -// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but -// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a -// QObject type used in assignment or when we don't have a QQmlEngine etc. -// -// This class does NOT reference the propertycache. -class QQmlEnginePrivate; -class Q_QML_EXPORT QQmlMetaObject -{ -public: - typedef QVarLengthArray<int, 9> ArgTypeStorage; - - inline QQmlMetaObject(); - inline QQmlMetaObject(QObject *); - inline QQmlMetaObject(const QMetaObject *); - inline QQmlMetaObject(QQmlPropertyCache *); - inline QQmlMetaObject(const QQmlMetaObject &); - - inline QQmlMetaObject &operator=(const QQmlMetaObject &); - - inline bool isNull() const; - - inline const char *className() const; - inline int propertyCount() const; - - inline bool hasMetaObject() const; - inline const QMetaObject *metaObject() const; - - QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; - - int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; - int *methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const; - - static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); - - // 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); - -protected: - QBiPointer<QQmlPropertyCache, const QMetaObject> _m; - int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const; - -}; - -class QQmlObjectOrGadget: public QQmlMetaObject -{ -public: - QQmlObjectOrGadget(QObject *obj) - : QQmlMetaObject(obj), - ptr(obj) - {} - QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget) - : QQmlMetaObject(propertyCache) - , ptr(gadget) - {} - - void metacall(QMetaObject::Call type, int index, void **argv) const; - -private: - QBiPointer<QObject, void> ptr; - -protected: - QQmlObjectOrGadget(const QMetaObject* metaObject) - : QQmlMetaObject(metaObject) - {} - -}; - -class QQmlStaticMetaObject : public QQmlObjectOrGadget { -public: - QQmlStaticMetaObject(const QMetaObject* metaObject) - : QQmlObjectOrGadget(metaObject) - {} - int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; -}; - -QQmlPropertyRawData::Flags::Flags() - : _otherBits(0) - , isConstant(false) - , isWritable(false) - , isResettable(false) - , isAlias(false) - , isFinal(false) - , isOverridden(false) - , isDirect(false) - , type(OtherType) - , isVMEFunction(false) - , hasArguments(false) - , isSignal(false) - , isVMESignal(false) - , isV4Function(false) - , isSignalHandler(false) - , isOverload(false) - , isCloned(false) - , isConstructor(false) - , notFullyResolved(false) - , overrideIndexIsProperty(false) -{} - -bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const -{ - return isConstant == other.isConstant && - isWritable == other.isWritable && - isResettable == other.isResettable && - isAlias == other.isAlias && - isFinal == other.isFinal && - isOverridden == other.isOverridden && - type == other.type && - isVMEFunction == other.isVMEFunction && - hasArguments == other.hasArguments && - isSignal == other.isSignal && - isVMESignal == other.isVMESignal && - isV4Function == other.isV4Function && - isSignalHandler == other.isSignalHandler && - isOverload == other.isOverload && - isCloned == other.isCloned && - isConstructor == other.isConstructor && - notFullyResolved == other.notFullyResolved && - overrideIndexIsProperty == other.overrideIndexIsProperty; -} - -void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from) -{ - switch (from.type) { - case QObjectDerivedType: - case EnumType: - case QListType: - case QmlBindingType: - case QJSValueType: - case V4HandleType: - case QVariantType: - type = from.type; - } -} - -QQmlPropertyData::QQmlPropertyData() -{ - setCoreIndex(-1); - setPropType(0); - setNotifyIndex(-1); - setOverrideIndex(-1); - setRevision(0); - setMetaObjectOffset(-1); - setArguments(nullptr); - trySetStaticMetaCallFunction(nullptr, 0); -} - -QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) -{ - *(static_cast<QQmlPropertyRawData *>(this)) = d; -} - -bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) -{ - return flags() == other.flags() && - propType() == other.propType() && - coreIndex() == other.coreIndex() && - notifyIndex() == other.notifyIndex() && - revision() == other.revision(); -} - inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const { if (p && Q_UNLIKELY(p->notFullyResolved())) @@ -880,124 +401,6 @@ bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const return false; } -QQmlMetaObject::QQmlMetaObject() -{ -} - -QQmlMetaObject::QQmlMetaObject(QObject *o) -{ - if (o) { - QQmlData *ddata = QQmlData::get(o, false); - if (ddata && ddata->propertyCache) _m = ddata->propertyCache; - else _m = o->metaObject(); - } -} - -QQmlMetaObject::QQmlMetaObject(const QMetaObject *m) -: _m(m) -{ -} - -QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m) -: _m(m) -{ -} - -QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o) -: _m(o._m) -{ -} - -QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o) -{ - _m = o._m; - return *this; -} - -bool QQmlMetaObject::isNull() const -{ - return _m.isNull(); -} - -const char *QQmlMetaObject::className() const -{ - if (_m.isNull()) { - return nullptr; - } else if (_m.isT1()) { - return _m.asT1()->className(); - } else { - return _m.asT2()->className(); - } -} - -int QQmlMetaObject::propertyCount() const -{ - if (_m.isNull()) { - return 0; - } else if (_m.isT1()) { - return _m.asT1()->propertyCount(); - } else { - return _m.asT2()->propertyCount(); - } -} - -bool QQmlMetaObject::hasMetaObject() const -{ - return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject()); -} - -const QMetaObject *QQmlMetaObject::metaObject() const -{ - if (_m.isNull()) return nullptr; - if (_m.isT1()) return _m.asT1()->createMetaObject(); - else return _m.asT2(); -} - -class QQmlPropertyCacheVector -{ -public: - QQmlPropertyCacheVector() {} - QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) - : data(std::move(other.data)) {} - QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { - QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data)); - data.swap(moved); - return *this; - } - - ~QQmlPropertyCacheVector() { clear(); } - void resize(int size) { return data.resize(size); } - int count() const { return data.count(); } - void clear() - { - for (int i = 0; i < data.count(); ++i) { - if (QQmlPropertyCache *cache = data.at(i).data()) - cache->release(); - } - data.clear(); - } - - void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } - QQmlPropertyCache *at(int index) const { return data.at(index).data(); } - void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { - if (QQmlPropertyCache *oldCache = data.at(index).data()) { - if (replacement.data() == oldCache) - return; - oldCache->release(); - } - data[index] = replacement.data(); - replacement->addref(); - } - - void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } - bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } -private: - Q_DISABLE_COPY(QQmlPropertyCacheVector) - QVector<QFlagPointer<QQmlPropertyCache>> data; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) - QT_END_NAMESPACE #endif // QQMLPROPERTYCACHE_P_H diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp new file mode 100644 index 0000000000..034ebfc743 --- /dev/null +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertycachecreator_p.h" + +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); + + +int QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::BuiltinType type) +{ + switch (type) { + case QV4::CompiledData::BuiltinType::Var: return QMetaType::QVariant; + case QV4::CompiledData::BuiltinType::Variant: return QMetaType::QVariant; + case QV4::CompiledData::BuiltinType::Int: return QMetaType::Int; + case QV4::CompiledData::BuiltinType::Bool: return QMetaType::Bool; + case QV4::CompiledData::BuiltinType::Real: return QMetaType::Double; + case QV4::CompiledData::BuiltinType::String: return QMetaType::QString; + case QV4::CompiledData::BuiltinType::Url: return QMetaType::QUrl; + case QV4::CompiledData::BuiltinType::Color: return QMetaType::QColor; + case QV4::CompiledData::BuiltinType::Font: return QMetaType::QFont; + case QV4::CompiledData::BuiltinType::Time: return QMetaType::QTime; + case QV4::CompiledData::BuiltinType::Date: return QMetaType::QDate; + case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::QDateTime; + case QV4::CompiledData::BuiltinType::Rect: return QMetaType::QRectF; + case QV4::CompiledData::BuiltinType::Point: return QMetaType::QPointF; + case QV4::CompiledData::BuiltinType::Size: return QMetaType::QSizeF; + case QV4::CompiledData::BuiltinType::Vector2D: return QMetaType::QVector2D; + case QV4::CompiledData::BuiltinType::Vector3D: return QMetaType::QVector3D; + case QV4::CompiledData::BuiltinType::Vector4D: return QMetaType::QVector4D; + case QV4::CompiledData::BuiltinType::Matrix4x4: return QMetaType::QMatrix4x4; + case QV4::CompiledData::BuiltinType::Quaternion: return QMetaType::QQuaternion; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: break; + }; + return QMetaType::UnknownType; +} + +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, + const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache) + : referencingObjectIndex(referencingObjectIndex) + , instantiatingBinding(instantiatingBinding) + , instantiatingPropertyName(instantiatingPropertyName) + , referencingObjectPropertyCache(referencingObjectPropertyCache) +{ +} + +bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() +{ + if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty) + return true; + + Q_ASSERT(referencingObjectIndex >= 0); + Q_ASSERT(referencingObjectPropertyCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache) + .property(instantiatingPropertyName, ¬InRevision, + QQmlPropertyResolver::IgnoreRevision); + return instantiatingProperty != nullptr; +} + +QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const +{ + if (instantiatingProperty) { + if (instantiatingProperty->isQObject()) { + return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion()); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) { + return enginePrivate->cache(vtmo, instantiatingProperty->typeMinorVersion()); + } + } + return QQmlRefPointer<QQmlPropertyCache>(); +} + +void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const +{ + for (QQmlBindingInstantiationContext pendingBinding: *this) { + const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex; + + if (propertyCaches->at(groupPropertyObjectIndex)) + continue; + + if (!pendingBinding.resolveInstantiatingProperty()) + continue; + + auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); + propertyCaches->set(groupPropertyObjectIndex, cache); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h new file mode 100644 index 0000000000..39778aa328 --- /dev/null +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -0,0 +1,872 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYCACHECREATOR_P_H +#define QQMLPROPERTYCACHECREATOR_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 <private/qqmlvaluetype_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlmetaobject_p.h> +#include <private/qqmlpropertyresolver_p.h> +#include <private/qqmltypedata_p.h> + +QT_BEGIN_NAMESPACE + +inline QQmlJS::DiagnosticMessage qQmlCompileError(const QV4::CompiledData::Location &location, + const QString &description) +{ + QQmlJS::DiagnosticMessage error; + error.line = location.line; + error.column = location.column; + error.message = description; + return error; +} + +struct QQmlBindingInstantiationContext { + QQmlBindingInstantiationContext() {} + QQmlBindingInstantiationContext(int referencingObjectIndex, + const QV4::CompiledData::Binding *instantiatingBinding, + const QString &instantiatingPropertyName, + QQmlPropertyCache *referencingObjectPropertyCache); + + bool resolveInstantiatingProperty(); + QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; + + int referencingObjectIndex = -1; + const QV4::CompiledData::Binding *instantiatingBinding = nullptr; + QString instantiatingPropertyName; + QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache; + QQmlPropertyData *instantiatingProperty = nullptr; +}; + +struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext> +{ + void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; +}; + +struct QQmlPropertyCacheCreatorBase +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) +public: + static QAtomicInt classIndexCounter; + + static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type); +}; + +template <typename ObjectContainer> +class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase +{ +public: + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, + QQmlEnginePrivate *enginePrivate, + const ObjectContainer *objectContainer, const QQmlImports *imports); + + QQmlJS::DiagnosticMessage buildMetaObjects(); + +protected: + QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); + QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const; + QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); + + int metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, QString *customTypeName = nullptr); + + QString stringAt(int index) const { return objectContainer->stringAt(index); } + + QQmlEnginePrivate * const enginePrivate; + const ObjectContainer * const objectContainer; + const QQmlImports * const imports; + QQmlPropertyCacheVector *propertyCaches; + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; +}; + +template <typename ObjectContainer> +inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, + QQmlEnginePrivate *enginePrivate, + const ObjectContainer *objectContainer, const QQmlImports *imports) + : enginePrivate(enginePrivate) + , objectContainer(objectContainer) + , imports(imports) + , propertyCaches(propertyCaches) + , pendingGroupPropertyBindings(pendingGroupPropertyBindings) +{ + propertyCaches->resize(objectContainer->objectCount()); +} + +template <typename ObjectContainer> +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() +{ + QQmlBindingInstantiationContext context; + return buildMetaObjectRecursively(/*root object*/0, context); +} + +template <typename ObjectContainer> +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +{ + auto isAddressable = [](const QUrl &url) { + const QString fileName = url.fileName(); + return !fileName.isEmpty() && fileName.front().isUpper(); + }; + + const CompiledObject *obj = objectContainer->objectAt(objectIndex); + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 + || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 + || (((obj->flags & QV4::CompiledData::Object::IsComponent) + || (objectIndex == 0 && isAddressable(objectContainer->url()))) + && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType); + + if (!needVMEMetaObject) { + auto binding = obj->bindingsBegin(); + auto end = obj->bindingsEnd(); + for ( ; binding != end; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + // If the on assignment is inside a group property, we need to distinguish between QObject based + // group properties and value type group properties. For the former the base type is derived from + // the property that references us, for the latter we only need a meta-object on the referencing object + // because interceptors can't go to the shared value type instances. + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) { + if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { + const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); + auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlJS::DiagnosticMessage error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + if (error.isValid()) + return error; + } + } else { + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + } + break; + } + } + } + + QQmlRefPointer<QQmlPropertyCache> baseTypeCache; + { + QQmlJS::DiagnosticMessage error; + baseTypeCache = propertyCacheForObject(obj, context, &error); + if (error.isValid()) + return error; + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + QQmlJS::DiagnosticMessage error = createMetaObject(objectIndex, obj, baseTypeCache); + if (error.isValid()) + return error; + } else { + propertyCaches->set(objectIndex, baseTypeCache); + } + } + + if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { + auto binding = obj->bindingsBegin(); + auto end = obj->bindingsEnd(); + for ( ; binding != end; ++binding) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + + // Binding to group property where we failed to look up the type of the + // property? Possibly a group property that is an alias that's not resolved yet. + // Let's attempt to resolve it after we're done with the aliases and fill in the + // propertyCaches entry then. + if (!context.resolveInstantiatingProperty()) + pendingGroupPropertyBindings->append(context); + + QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context); + if (error.isValid()) + return error; + } + } + + QQmlJS::DiagnosticMessage noError; + return noError; +} + +template <typename ObjectContainer> +inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const +{ + if (context.instantiatingProperty) { + return context.instantiatingPropertyCache(enginePrivate); + } else if (obj->inheritedTypeNameIndex != 0) { + auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->isFullyDynamicType) { + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); + return nullptr; + } + if (obj->signalCount() > 0) { + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); + return nullptr; + } + if (obj->functionCount() > 0) { + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); + return nullptr; + } + } + + return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { + auto *typeRef = objectContainer->resolvedType( + context.instantiatingBinding->propertyNameIndex); + Q_ASSERT(typeRef); + QQmlType qmltype = typeRef->type; + if (!qmltype.isValid()) { + imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex), + &qmltype, nullptr, nullptr, nullptr); + } + + const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); + if (!attachedMo) { + *error = qQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); + return nullptr; + } + return enginePrivate->cache(attachedMo); + } + return nullptr; +} + +template <typename ObjectContainer> +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) +{ + QQmlRefPointer<QQmlPropertyCache> cache; + cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount())); + + propertyCaches->set(objectIndex, cache); + propertyCaches->setNeedsVMEMetaObject(objectIndex); + + QByteArray newClassName; + + if (objectIndex == /*root object*/0) { + const QString path = objectContainer->url().path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache.data()).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int varPropCount = 0; + + QQmlPropertyResolver resolver(baseTypeCache); + + auto p = obj->propertiesBegin(); + auto pend = obj->propertiesEnd(); + for ( ; p != pend; ++p) { + if (p->builtinType() == QV4::CompiledData::BuiltinType::Var) + varPropCount++; + + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + auto a = obj->aliasesBegin(); + auto aend = obj->aliasesEnd(); + for ( ; a != aend; ++a) { + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // 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"); + QQmlPropertyCache *parentCache = cache.data(); + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // Set up notify signals for properties - first normal, then alias + p = obj->propertiesBegin(); + pend = obj->propertiesEnd(); + for ( ; p != pend; ++p) { + auto flags = QQmlPropertyData::defaultSignalFlags(); + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + a = obj->aliasesBegin(); + aend = obj->aliasesEnd(); + for ( ; a != aend; ++a) { + auto flags = QQmlPropertyData::defaultSignalFlags(); + + QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + auto e = obj->enumsBegin(); + auto eend = obj->enumsEnd(); + for ( ; e != eend; ++e) { + const int enumValueCount = e->enumValueCount(); + QVector<QQmlEnumValue> values; + values.reserve(enumValueCount); + + auto enumValue = e->enumValuesBegin(); + auto end = e->enumValuesEnd(); + for ( ; enumValue != end; ++enumValue) + values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value)); + + cache->appendEnum(stringAt(e->nameIndex), values); + } + + // Dynamic signals + auto s = obj->signalsBegin(); + auto send = obj->signalsEnd(); + for ( ; s != send; ++s) { + const int paramCount = s->parameterCount(); + + QList<QByteArray> names; + names.reserve(paramCount); + QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + int i = 0; + auto param = s->parametersBegin(); + auto end = s->parametersEnd(); + for ( ; param != end; ++param, ++i) { + names.append(stringAt(param->nameIndex).toUtf8()); + + QString customTypeName; + auto type = metaTypeForParameter(param->type, &customTypeName); + if (type == QMetaType::UnknownType) + return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); + + paramTypes[i + 1] = type; + } + } + + auto flags = QQmlPropertyData::defaultSignalFlags(); + if (paramCount) + flags.hasArguments = 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); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():nullptr, names); + } + + + // Dynamic slots + auto function = objectContainer->objectFunctionsBegin(obj); + auto fend = objectContainer->objectFunctionsEnd(obj); + for ( ; function != fend; ++function) { + 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")); + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + QList<QByteArray> parameterNames; + QVector<int> parameterTypes; + auto formal = function->formalsBegin(); + auto end = function->formalsEnd(); + for ( ; formal != end; ++formal) { + flags.hasArguments = true; + parameterNames << stringAt(formal->nameIndex).toUtf8(); + int type = metaTypeForParameter(formal->type); + if (type == QMetaType::UnknownType) + type = QMetaType::QVariant; + parameterTypes << type; + } + + int returnType = metaTypeForParameter(function->returnType); + if (returnType == QMetaType::UnknownType) + returnType = QMetaType::QVariant; + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, returnType, parameterNames, parameterTypes); + } + + + // Dynamic properties + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + p = obj->propertiesBegin(); + pend = obj->propertiesEnd(); + for ( ; p != pend; ++p, ++propertyIdx) { + int propertyType = 0; + int propertTypeMinorVersion = 0; + QQmlPropertyData::Flags propertyFlags; + + const QV4::CompiledData::BuiltinType type = p->builtinType(); + + if (type == QV4::CompiledData::BuiltinType::Var) + propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; + + + if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) { + propertyType = metaTypeForPropertyType(type); + + if (type == QV4::CompiledData::BuiltinType::Variant) + propertyFlags.type = QQmlPropertyData::Flags::QVariantType; + } else { + Q_ASSERT(!p->isBuiltinType); + + QQmlType qmltype; + if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) { + return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); + } + + Q_ASSERT(qmltype.isValid()); + if (qmltype.isComposite()) { + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + if (p->isList) { + propertyType = compilationUnit->listMetaTypeId; + } else { + propertyType = compilationUnit->metaTypeId; + } + } else { + if (p->isList) { + propertyType = qmltype.qListTypeId(); + } else { + propertyType = qmltype.typeId(); + propertTypeMinorVersion = qmltype.minorVersion(); + } + } + + if (p->isList) + propertyFlags.type = QQmlPropertyData::Flags::QListType; + else + propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; + } + + if (!p->isReadOnly && !p->isList) + propertyFlags.isWritable = true; + + + QString propertyName = stringAt(p->nameIndex); + if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, propertTypeMinorVersion, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + QQmlJS::DiagnosticMessage noError; + return noError; +} + +template <typename ObjectContainer> +inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, + QString *customTypeName) +{ + if (param.indexIsBuiltinType) { + // built-in type + return metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType))); + } + + // lazily resolved type + const QString typeName = stringAt(param.typeNameIndexOrBuiltinType); + if (customTypeName) + *customTypeName = typeName; + QQmlType qmltype; + if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr)) + return QMetaType::UnknownType; + + if (!qmltype.isComposite()) + return qmltype.typeId(); + + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + return compilationUnit->metaTypeId; +} + +template <typename ObjectContainer> +class QQmlPropertyCacheAliasCreator +{ +public: + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); + + void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv); + + QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv); + +private: + void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv); + QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv); + + void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; + + int objectForId(const CompiledObject &component, int id) const; + + QQmlPropertyCacheVector *propertyCaches; + const ObjectContainer *objectContainer; +}; + +template <typename 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.flags & 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->flags & QV4::CompiledData::Alias::Resolved); + + const int targetObjectIndex = objectForId(component, alias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + if (alias->aliasToLocalAlias) + continue; + + if (alias->encodedMetaPropertyIndex == -1) + continue; + + const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + + int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex(); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + if (!targetProperty) + return false; + } + return true; + }; + + do { + QVector<int> pendingObjects; + + for (int objectIndex: qAsConst(objectsWithAliases)) { + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + + if (allAliasTargetsExist(object)) { + appendAliasesToPropertyCache(component, objectIndex, enginePriv); + } else { + pendingObjects.append(objectIndex); + } + + } + qSwap(objectsWithAliases, 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.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) + return; + + auto binding = object.bindingsBegin(); + auto end = object.bindingsEnd(); + for (; binding != end; ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); + } +} + +template <typename ObjectContainer> +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, + QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv) +{ + *type = 0; + bool writable = false; + bool resettable = false; + + propertyFlags->isAlias = true; + + if (alias.aliasToLocalAlias) { + const QV4::CompiledData::Alias *lastAlias = &alias; + QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias}); + + do { + const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); + Q_ASSERT(targetObject); + + auto nextAlias = targetObject->aliasesBegin(); + for (uint i = 0; i < lastAlias->localAliasIndex; ++i) + ++nextAlias; + + const QV4::CompiledData::Alias *targetAlias = &(*nextAlias); + if (seenAliases.contains(targetAlias)) { + return qQmlCompileError(targetAlias->location, + QQmlPropertyCacheCreatorBase::tr("Cyclic alias")); + } + + seenAliases.append(targetAlias); + lastAlias = targetAlias; + } while (lastAlias->aliasToLocalAlias); + + return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv); + } + + const int targetObjectIndex = objectForId(component, alias.targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); + + if (alias.encodedMetaPropertyIndex == -1) { + Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); + auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex); + if (!typeRef) { + // Can be caused by the alias target not being a valid id or property. E.g.: + // property alias dataValue: dataVal + // invalidAliasComponent { id: dataVal } + return qQmlCompileError(targetObject.location, + QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + } + + if (typeRef->type.isValid()) + *type = typeRef->type.typeId(); + else + *type = typeRef->compilationUnit->metaTypeId; + + *minorVersion = typeRef->minorVersion; + + propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; + } else { + int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); + int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex(); + + QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + Q_ASSERT(targetProperty); + + // for deep aliases, valueTypeIndex is always set + if (!QQmlValueTypeFactory::isValueType(targetProperty->propType()) && valueTypeIndex != -1) { + // deep alias property + *type = targetProperty->propType(); + targetCache = enginePriv->propertyCacheForType(*type); + Q_ASSERT(targetCache); + targetProperty = targetCache->property(valueTypeIndex); + + + *type = targetProperty->propType(); + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + } else { + // value type or primitive type or enum + *type = targetProperty->propType(); + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + } else { + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); + + if (targetProperty->isVarProperty()) + propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + } + } + } + } + + propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable; + propertyFlags->isResettable = resettable; + return QQmlJS::DiagnosticMessage(); +} + +template <typename ObjectContainer> +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( + const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv) +{ + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + if (!object.aliasCount()) + return QQmlJS::DiagnosticMessage(); + + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + Q_ASSERT(propertyCache); + + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + + int aliasIndex = 0; + auto alias = object.aliasesBegin(); + auto end = object.aliasesEnd(); + for ( ; alias != end; ++alias, ++aliasIndex) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + + int type = 0; + int minorVersion = 0; + QQmlPropertyData::Flags propertyFlags; + QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv); + if (error.isValid()) + return error; + + const QString propertyName = objectContainer->stringAt(alias->nameIndex); + + if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) + propertyCache->_defaultPropertyName = propertyName; + + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, minorVersion, effectiveSignalIndex++); + } + + return QQmlJS::DiagnosticMessage(); +} + +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.id == id) + return candidateIndex; + } + return -1; +} + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/qml/qqmlpropertycachemethodarguments_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h new file mode 100644 index 0000000000..62f09bdfff --- /dev/null +++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYCACHEMETODARGUMENTS_P_H +#define QQMLPROPERTYCACHEMETODARGUMENTS_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/qlist.h> +#include <QtCore/qbytearray.h> + +QT_BEGIN_NAMESPACE + +class QString; +class QQmlPropertyCacheMethodArguments +{ +public: + QQmlPropertyCacheMethodArguments *next; + + //for signal handler rewrites + QString *signalParameterStringForJS; + int parameterError:1; + int argumentsValid:1; + + QList<QByteArray> *names; + + int arguments[1]; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHEMETODARGUMENTS_P_H diff --git a/src/qml/qml/qqmlpropertycachevector_p.h b/src/qml/qml/qqmlpropertycachevector_p.h new file mode 100644 index 0000000000..1dff7c61a6 --- /dev/null +++ b/src/qml/qml/qqmlpropertycachevector_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYCACHEVECTOR_P_H +#define QQMLPROPERTYCACHEVECTOR_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 <private/qflagpointer_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyCacheVector +{ +public: + QQmlPropertyCacheVector() {} + QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) + : data(std::move(other.data)) {} + QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { + QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data)); + data.swap(moved); + return *this; + } + + ~QQmlPropertyCacheVector() { clear(); } + void resize(int size) { return data.resize(size); } + int count() const { return data.count(); } + void clear() + { + for (int i = 0; i < data.count(); ++i) { + if (QQmlPropertyCache *cache = data.at(i).data()) + cache->release(); + } + data.clear(); + } + + void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } + QQmlPropertyCache *at(int index) const { return data.at(index).data(); } + void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { + if (QQmlPropertyCache *oldCache = data.at(index).data()) { + if (replacement.data() == oldCache) + return; + oldCache->release(); + } + data[index] = replacement.data(); + replacement->addref(); + } + + void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } + bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } +private: + Q_DISABLE_COPY(QQmlPropertyCacheVector) + QVector<QFlagPointer<QQmlPropertyCache>> data; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHEVECTOR_P_H diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h new file mode 100644 index 0000000000..dec696226e --- /dev/null +++ b/src/qml/qml/qqmlpropertydata_p.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYDATA_P_H +#define QQMLPROPERTYDATA_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 <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyCacheMethodArguments; +class QQmlPropertyData +{ +public: + enum WriteFlag { + BypassInterceptor = 0x01, + DontRemoveBinding = 0x02, + RemoveBindingOnAliasWrite = 0x04 + }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + + typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; + + struct Flags { + enum Types { + 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, // Property type is a QQmlBinding* + 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 + }; + + // 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. + // (Note: this padding is done here, because certain compilers have surprising behavior + // when an enum is declared in-between two bit fields.) + enum { BitsLeftInFlags = 10 }; + unsigned otherBits : BitsLeftInFlags; // align to 32 bits + + // Can apply to all properties, except IsFunction + unsigned isConstant : 1; // Has CONST flag + unsigned isWritable : 1; // Has WRITE function + unsigned isResettable : 1; // Has RESET function + unsigned isAlias : 1; // Is a QML alias to another property + unsigned isFinal : 1; // Has FINAL flag + unsigned isOverridden : 1; // Is overridden by a extension property + unsigned isDirect : 1; // Exists on a C++ QMetaObject + + unsigned type : 4; // stores an entry of Types + + // Apply only to IsFunctions + unsigned isVMEFunction : 1; // Function was added by QML + unsigned hasArguments : 1; // Function takes arguments + unsigned isSignal : 1; // Function is a signal + unsigned isVMESignal : 1; // Signal was added by QML + unsigned isV4Function : 1; // Function takes QQmlV4Function* args + unsigned isSignalHandler : 1; // Function is a signal handler + unsigned isOverload : 1; // Function is an overload of another function + unsigned isCloned : 1; // The function was marked as cloned + unsigned isConstructor : 1; // The function was marked is a constructor + + // Internal QQmlPropertyCache flags + unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved + unsigned overrideIndexIsProperty: 1; + + inline Flags(); + inline bool operator==(const Flags &other) const; + inline void copyPropertyTypeFlags(Flags from); + }; + + inline bool operator==(const QQmlPropertyData &) const; + + Flags flags() const { return m_flags; } + void setFlags(Flags f) + { + unsigned otherBits = m_flags.otherBits; + m_flags = f; + m_flags.otherBits = otherBits; + } + + bool isValid() const { return coreIndex() != -1; } + + bool isConstant() const { return m_flags.isConstant; } + bool isWritable() const { return m_flags.isWritable; } + void setWritable(bool onoff) { m_flags.isWritable = onoff; } + bool isResettable() const { return m_flags.isResettable; } + bool isAlias() const { return m_flags.isAlias; } + bool isFinal() const { return m_flags.isFinal; } + bool isOverridden() const { return m_flags.isOverridden; } + bool isDirect() const { return m_flags.isDirect; } + bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } + bool isFunction() const { return m_flags.type == Flags::FunctionType; } + 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 isQmlBinding() const { return m_flags.type == Flags::QmlBindingType; } + 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 m_flags.isVMEFunction; } + bool hasArguments() const { return m_flags.hasArguments; } + bool isSignal() const { return m_flags.isSignal; } + bool isVMESignal() const { return m_flags.isVMESignal; } + bool isV4Function() const { return m_flags.isV4Function; } + bool isSignalHandler() const { return m_flags.isSignalHandler; } + bool isOverload() const { return m_flags.isOverload; } + void setOverload(bool onoff) { m_flags.isOverload = onoff; } + bool isCloned() const { return m_flags.isCloned; } + bool isConstructor() const { return m_flags.isConstructor; } + + bool hasOverride() const { return overrideIndex() >= 0; } + bool hasRevision() const { return revision() != 0; } + + bool isFullyResolved() const { return !m_flags.notFullyResolved; } + + int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; } + void setPropType(int pt) + { + Q_ASSERT(pt >= 0); + Q_ASSERT(pt <= std::numeric_limits<qint16>::max()); + m_propType = quint16(pt); + } + + int notifyIndex() const { return m_notifyIndex; } + void setNotifyIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_notifyIndex = qint16(idx); + } + + bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; } + + int overrideIndex() const { return m_overrideIndex; } + void setOverrideIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_overrideIndex = qint16(idx); + } + + int coreIndex() const { return m_coreIndex; } + void setCoreIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_coreIndex = qint16(idx); + } + + quint8 revision() const { return m_revision; } + void setRevision(quint8 rev) + { + Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); + m_revision = quint8(rev); + } + + /* If a property is a C++ type, then we store the minor + * version of this type. + * This is required to resolve property or signal revisions + * if this property is used as a grouped property. + * + * Test.qml + * property TextEdit someTextEdit: TextEdit {} + * + * Test { + * someTextEdit.preeditText: "test" //revision 7 + * someTextEdit.onEditingFinished: console.log("test") //revision 6 + * } + * + * To determine if these properties with revisions are available we need + * the minor version of TextEdit as imported in Test.qml. + * + */ + + quint8 typeMinorVersion() const { return m_typeMinorVersion; } + void setTypeMinorVersion(quint8 rev) + { + Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); + m_typeMinorVersion = quint8(rev); + } + + QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } + void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; } + + int metaObjectOffset() const { return m_metaObjectOffset; } + void setMetaObjectOffset(int off) + { + Q_ASSERT(off >= std::numeric_limits<qint16>::min()); + Q_ASSERT(off <= std::numeric_limits<qint16>::max()); + m_metaObjectOffset = qint16(off); + } + + StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; } + void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) + { + if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { + m_flags.otherBits = relativePropertyIndex; + m_staticMetaCallFunction = f; + } + } + quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; } + + static Flags flagsForProperty(const QMetaProperty &); + void load(const QMetaProperty &); + void load(const QMetaMethod &); + QString name(QObject *) const; + QString name(const QMetaObject *) const; + + void markAsOverrideOf(QQmlPropertyData *predecessor); + + inline void readProperty(QObject *target, void *property) const + { + void *args[] = { property, nullptr }; + readPropertyWithArgs(target, args); + } + + inline void readPropertyWithArgs(QObject *target, void *args[]) const + { + if (hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); + else if (isDirect()) + target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); + else + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); + } + + bool writeProperty(QObject *target, void *value, WriteFlags flags) const + { + int status = -1; + void *argv[] = { value, nullptr, &status, &flags }; + if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); + else if (flags.testFlag(BypassInterceptor) && isDirect()) + target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); + else + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); + return true; + } + + static Flags defaultSignalFlags() + { + Flags f; + f.isSignal = true; + f.type = Flags::FunctionType; + f.isVMESignal = true; + return f; + } + + static Flags defaultSlotFlags() + { + Flags f; + f.type = Flags::FunctionType; + f.isVMEFunction = true; + return f; + } + +private: + friend class QQmlPropertyCache; + void lazyLoad(const QMetaProperty &); + void lazyLoad(const QMetaMethod &); + bool notFullyResolved() const { return m_flags.notFullyResolved; } + + Flags m_flags; + qint16 m_coreIndex = -1; + quint16 m_propType = 0; + + // The notify index is in the range returned by QObjectPrivate::signalIndex(). + // This is different from QMetaMethod::methodIndex(). + qint16 m_notifyIndex = -1; + qint16 m_overrideIndex = -1; + + quint8 m_revision = 0; + quint8 m_typeMinorVersion = 0; + qint16 m_metaObjectOffset = -1; + + QQmlPropertyCacheMethodArguments *m_arguments = nullptr; + StaticMetaCallFunction m_staticMetaCallFunction = nullptr; +}; + +#if QT_POINTER_SIZE == 4 + Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24); +#else // QT_POINTER_SIZE == 8 + Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32); +#endif + +bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const +{ + return flags() == other.flags() && + propType() == other.propType() && + coreIndex() == other.coreIndex() && + notifyIndex() == other.notifyIndex() && + revision() == other.revision(); +} + +QQmlPropertyData::Flags::Flags() + : otherBits(0) + , isConstant(false) + , isWritable(false) + , isResettable(false) + , isAlias(false) + , isFinal(false) + , isOverridden(false) + , isDirect(false) + , type(OtherType) + , isVMEFunction(false) + , hasArguments(false) + , isSignal(false) + , isVMESignal(false) + , isV4Function(false) + , isSignalHandler(false) + , isOverload(false) + , isCloned(false) + , isConstructor(false) + , notFullyResolved(false) + , overrideIndexIsProperty(false) +{} + +bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const +{ + return isConstant == other.isConstant && + isWritable == other.isWritable && + isResettable == other.isResettable && + isAlias == other.isAlias && + isFinal == other.isFinal && + isOverridden == other.isOverridden && + type == other.type && + isVMEFunction == other.isVMEFunction && + hasArguments == other.hasArguments && + isSignal == other.isSignal && + isVMESignal == other.isVMESignal && + isV4Function == other.isV4Function && + isSignalHandler == other.isSignalHandler && + isOverload == other.isOverload && + isCloned == other.isCloned && + isConstructor == other.isConstructor && + notFullyResolved == other.notFullyResolved && + overrideIndexIsProperty == other.overrideIndexIsProperty; +} + +void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from) +{ + switch (from.type) { + case QObjectDerivedType: + case EnumType: + case QListType: + case QmlBindingType: + case QJSValueType: + case QVariantType: + type = from.type; + } +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYDATA_P_H diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp new file mode 100644 index 0000000000..90eaca0b90 --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyresolver_p.h" + +QT_BEGIN_NAMESPACE + +QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision, + RevisionCheck check) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + + // Find the first property + while (d && d->isFunction()) + d = cache->overrideData(d); + + if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else { + return d; + } +} + + +QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + if (notInRevision) *notInRevision = false; + + while (d && !(d->isFunction())) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else if (d && d->isSignal()) { + return d; + } + + if (name.endsWith(QLatin1String("Changed"))) { + QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); + + d = property(propName, notInRevision); + if (d) + return cache->signal(d->notifyIndex()); + } + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyresolver_p.h b/src/qml/qml/qqmlpropertyresolver_p.h new file mode 100644 index 0000000000..df857f242e --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYRESOLVER_P_H +#define QQMLPROPERTYRESOLVER_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 <private/qtqmlglobal_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlrefcount_p.h> + +QT_BEGIN_NAMESPACE + +struct Q_QML_EXPORT QQmlPropertyResolver +{ + QQmlPropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) + : cache(cache) + {} + + QQmlPropertyData *property(int index) const + { + return cache->property(index); + } + + enum RevisionCheck { + CheckRevision, + IgnoreRevision + }; + + QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, + RevisionCheck check = CheckRevision) const; + + // This code must match the semantics of QQmlPropertyPrivate::findSignalByName + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; + + QQmlRefPointer<QQmlPropertyCache> cache; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYRESOLVER_P_H diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp new file mode 100644 index 0000000000..ef85d6e4d9 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -0,0 +1,770 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyvalidator_p.h" + +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlirbuilder_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmlpropertyresolver_p.h> + +#include <QtCore/qdatetime.h> + +QT_BEGIN_NAMESPACE + +static bool isPrimitiveType(int typeId) +{ + switch (typeId) { +#define HANDLE_PRIMITIVE(Type, id, T) \ + case QMetaType::Type: +QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE); +#undef HANDLE_PRIMITIVE + return true; + default: + return false; + } +} + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) + : enginePrivate(enginePrivate) + , compilationUnit(compilationUnit) + , imports(imports) + , qmlUnit(compilationUnit->unitData()) + , propertyCaches(compilationUnit->propertyCaches) + , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) +{ + bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); +} + +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validate() +{ + return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); +} + +typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; + +struct BindingFinder +{ + bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const + { + return name < binding->propertyNameIndex; + } + bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const + { + return binding->propertyNameIndex < name; + } + bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const + { + return lhs->propertyNameIndex < rhs->propertyNameIndex; + } +}; + +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( + int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +{ + const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); + + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->nBindings == 1); + const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + return validateObject(componentBinding->value.objectIndex, componentBinding); + } + + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + if (!propertyCache) + return QVector<QQmlJS::DiagnosticMessage>(); + + QQmlCustomParser *customParser = nullptr; + if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { + if (typeRef->type.isValid()) + customParser = typeRef->type.customParser(); + } + + QList<const QV4::CompiledData::Binding*> customBindings; + + // Collect group properties first for sanity checking + // vector values are sorted by property name string index. + GroupPropertyVector groupProperties; + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (!binding->isGroupProperty()) + continue; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + continue; + + if (populatingValueTypeGroupProperty) { + return recordError(binding->location, tr("Property assignment expected")); + } + + GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + groupProperties.insert(pos, binding); + } + + QQmlPropertyResolver propertyResolver(propertyCache); + + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = nullptr; + if (obj->indexOfDefaultPropertyOrAlias != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } + + QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + + binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + QString name = stringAt(binding->propertyNameIndex); + + if (customParser) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { + customBindings << binding; + continue; + } + } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + customBindings << binding; + continue; + } + } + + bool bindingToDefaultProperty = false; + bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; + + bool notInRevision = false; + QQmlPropertyData *pd = nullptr; + if (!name.isEmpty()) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { + pd = propertyResolver.signal(name, ¬InRevision); + } else { + pd = propertyResolver.property(name, ¬InRevision, + QQmlPropertyResolver::CheckRevision); + } + + if (notInRevision) { + QString typeName = stringAt(obj->inheritedTypeNameIndex); + auto *objectType = resolvedType(obj->inheritedTypeNameIndex); + if (objectType && objectType->type.isValid()) { + return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + } else { + return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); + } + } + } else { + if (isGroupProperty) + return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); + + pd = defaultProperty; + name = defaultPropertyName; + bindingToDefaultProperty = true; + } + + if (pd) + collectedBindingPropertyData[i] = pd; + + if (name.constData()->isUpper() && !binding->isAttachedProperty()) { + QQmlType type; + QQmlImportNamespace *typeNamespace = nullptr; + imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace); + if (typeNamespace) + return recordError(binding->location, tr("Invalid use of namespace")); + return recordError(binding->location, tr("Invalid attached object assignment")); + } + + if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + const bool populatingValueTypeGroupProperty + = pd + && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); + const QVector<QQmlJS::DiagnosticMessage> subObjectValidatorErrors + = validateObject(binding->value.objectIndex, binding, + populatingValueTypeGroupProperty); + if (!subObjectValidatorErrors.isEmpty()) + return subObjectValidatorErrors; + } + + // Signal handlers were resolved and checked earlier in the signal handler conversion pass. + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { + return recordError(binding->location, tr("Attached properties cannot be used here")); + } + continue; + } + + if (pd) { + GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); + + if (!pd->isWritable() + && !pd->isQList() + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + ) { + + if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) + return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); + return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + + if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { + QString error; + if (pd->propType() == qMetaTypeId<QQmlScriptString>()) + error = tr( "Cannot assign multiple values to a script property"); + else + error = tr( "Cannot assign multiple values to a singular property"); + return recordError(binding->valueLocation, error); + } + + if (!bindingToDefaultProperty + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && assigningToGroupProperty) { + QV4::CompiledData::Location loc = binding->valueLocation; + if (loc < (*assignedGroupProperty)->valueLocation) + loc = (*assignedGroupProperty)->valueLocation; + + if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) + return recordError(loc, tr("Property has already been assigned a value")); + return recordError(loc, tr("Cannot assign a value directly to a grouped property")); + } + + if (binding->type < QV4::CompiledData::Binding::Type_Script) { + QQmlJS::DiagnosticMessage bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingError.isValid()) + return recordError(bindingError); + } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { + QQmlJS::DiagnosticMessage bindingError = validateObjectBinding(pd, name, binding); + if (bindingError.isValid()) + return recordError(bindingError); + } else if (binding->isGroupProperty()) { + if (QQmlValueTypeFactory::isValueType(pd->propType())) { + if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { + if (!pd->isWritable()) { + return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + } else { + return recordError(binding->location, tr("Invalid grouped property access")); + } + } else { + const int typeId = pd->propType(); + if (isPrimitiveType(typeId)) { + return recordError( + binding->location, + tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".") + .arg(name) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) + ); + } + + if (!enginePrivate->propertyCacheForType(typeId)) { + return recordError(binding->location, + tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") + .arg(name) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) + ); + } + } + } + } else { + if (customParser) { + customBindings << binding; + continue; + } + if (bindingToDefaultProperty) { + return recordError(binding->location, tr("Cannot assign to non-existent default property")); + } else { + return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); + } + } + } + + if (obj->idNameIndex) { + if (populatingValueTypeGroupProperty) + return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type")); + + bool notInRevision = false; + collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); + } + + if (customParser && !customBindings.isEmpty()) { + customParser->clearErrors(); + customParser->validator = this; + customParser->engine = enginePrivate; + customParser->imports = &imports; + customParser->verifyBindings(compilationUnit, customBindings); + customParser->validator = nullptr; + customParser->engine = nullptr; + customParser->imports = (QQmlImports*)nullptr; + QVector<QQmlJS::DiagnosticMessage> parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) + return parserErrors; + } + + (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; + + QVector<QQmlJS::DiagnosticMessage> noError; + return noError; +} + +QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +{ + if (property->isQList()) { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); + } + + QQmlJS::DiagnosticMessage noError; + + if (property->isEnum()) { + if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + return noError; + + QString value = compilationUnit->bindingValueAsString(binding); + QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); + bool ok; + if (p.isFlagType()) { + p.enumerator().keysToValue(value.toUtf8().constData(), &ok); + } else + p.enumerator().keyToValue(value.toUtf8().constData(), &ok); + + if (!ok) { + return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); + } + return noError; + } + + auto warnOrError = [&](const QString &error) { + if (binding->type == QV4::CompiledData::Binding::Type_Null) { + QQmlError warning; + warning.setUrl(compilationUnit->url()); + warning.setLine(binding->valueLocation.line); + warning.setColumn(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); + }; + + switch (property->propType()) { + case QMetaType::QVariant: + break; + case QVariant::String: { + if (!binding->evaluatesToString()) { + return warnOrError(tr("Invalid property assignment: string expected")); + } + } + break; + case QVariant::StringList: { + if (!binding->evaluatesToString()) { + return warnOrError(tr("Invalid property assignment: string or string list expected")); + } + } + break; + case QVariant::ByteArray: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return warnOrError(tr("Invalid property assignment: byte array expected")); + } + } + break; + case QVariant::Url: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return warnOrError(tr("Invalid property assignment: url expected")); + } + } + break; + case QVariant::UInt: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = compilationUnit->bindingValueAsNumber(binding); + if (double(uint(d)) == d) + return noError; + } + return warnOrError(tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = compilationUnit->bindingValueAsNumber(binding); + if (double(int(d)) == d) + return noError; + } + return warnOrError(tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return warnOrError(tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Double: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return warnOrError(tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Color: { + bool ok = false; + QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: color expected")); + } + } + break; +#if QT_CONFIG(datestring) + case QVariant::Date: { + bool ok = false; + QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: date expected")); + } + } + break; + case QVariant::Time: { + bool ok = false; + QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: time expected")); + } + } + break; + case QVariant::DateTime: { + bool ok = false; + QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: datetime expected")); + } + } + break; +#endif // datestring + case QVariant::Point: { + bool ok = false; + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::PointF: { + bool ok = false; + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Size: { + bool ok = false; + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::SizeF: { + bool ok = false; + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::Rect: { + bool ok = false; + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: rect expected")); + } + } + break; + case QVariant::RectF: { + bool ok = false; + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (!ok) { + return warnOrError(tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Bool: { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return warnOrError(tr("Invalid property assignment: boolean expected")); + } + } + break; + case QVariant::Vector2D: { + struct { + float xp; + float yp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { + return warnOrError(tr("Invalid property assignment: 2D vector expected")); + } + } + break; + case QVariant::Vector3D: { + struct { + float xp; + float yp; + float zy; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { + return warnOrError(tr("Invalid property assignment: 3D vector expected")); + } + } + break; + case QVariant::Vector4D: { + struct { + float xp; + float yp; + float zy; + float wp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { + return warnOrError(tr("Invalid property assignment: 4D vector expected")); + } + } + break; + case QVariant::Quaternion: { + struct { + float wp; + float xp; + float yp; + float zp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { + return warnOrError(tr("Invalid property assignment: quaternion expected")); + } + } + break; + case QVariant::RegExp: + case QVariant::RegularExpression: + return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + default: { + // generate single literal value assignment to a list property if required + if (property->propType() == qMetaTypeId<QList<qreal> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); + } + break; + } else if (property->propType() == qMetaTypeId<QList<int> >()) { + bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); + if (ok) { + double n = compilationUnit->bindingValueAsNumber(binding); + if (double(int(n)) != n) + ok = false; + } + if (!ok) + return warnOrError(tr("Invalid property assignment: int or array of ints expected")); + break; + } else if (property->propType() == qMetaTypeId<QList<bool> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); + } + break; + } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return warnOrError(tr("Invalid property assignment: url or array of urls expected")); + } + break; + } else if (property->propType() == qMetaTypeId<QList<QString> >()) { + if (!binding->evaluatesToString()) { + return warnOrError(tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (property->propType() == qMetaTypeId<QJSValue>()) { + break; + } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { + break; + } else if (property->isQObject() + && binding->type == QV4::CompiledData::Binding::Type_Null) { + break; + } + + // otherwise, try a custom type assignment + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); + if (!converter) { + return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); + } + } + break; + } + return noError; +} + +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const +{ + QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); + + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->parent(); + } + return false; +} + +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +{ + QVector<QQmlJS::DiagnosticMessage> errors; + errors.append(qQmlCompileError(location, description)); + return errors; +} + +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QQmlJS::DiagnosticMessage &error) const +{ + QVector<QQmlJS::DiagnosticMessage> errors; + errors.append(error); + return errors; +} + +QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +{ + QQmlJS::DiagnosticMessage noError; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); + + bool isValueSource = false; + bool isPropertyInterceptor = false; + + const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex); + if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) { + QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + const QMetaObject *mo = cache->firstCppMetaObject(); + 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; + } + + if (!isValueSource && !isPropertyInterceptor) { + return qQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); + } + + return noError; + } + + const int propType = property->propType(); + const auto rhsType = [&]() { + return stringAt(compilationUnit->objectAt(binding->value.objectIndex) + ->inheritedTypeNameIndex); + }; + + if (QQmlMetaType::isInterface(propType)) { + // Can only check at instantiation time if the created sub-object successfully casts to the + // target interface. + return noError; + } else if (propType == QMetaType::QVariant || propType == qMetaTypeId<QJSValue>()) { + // We can convert everything to QVariant :) + return noError; + } else if (property->isQList()) { + const int listType = enginePrivate->listType(propType); + if (!QQmlMetaType::isInterface(listType)) { + QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); + if (!canCoerce(listType, source)) { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); + } + } + return noError; + } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { + return noError; + } else if (isPrimitiveType(propType)) { + auto typeName = QMetaType::typeName(propType); + return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting \"%3\"") + .arg(rhsType()) + .arg(propertyName) + .arg(typeName)); + } else if (QQmlValueTypeFactory::isValueType(propType)) { + return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting an object") + .arg(rhsType()).arg(propertyName)); + } else if (propType == qMetaTypeId<QQmlScriptString>()) { + return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); + } else { + // We want to use the raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions that might + // effect the properties on the type, but don't effect assignability + // Using -1 for the minor version ensures that we get the raw metaObject. + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(propType, -1); + + if (propertyMetaObject) { + // Will be true if the assigned type inherits propertyMetaObject + // Determine isAssignable value + bool isAssignable = false; + QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); + while (c && !isAssignable) { + isAssignable |= c == propertyMetaObject; + c = c->parent(); + } + + if (!isAssignable) { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") + .arg(rhsType()).arg(QLatin1String(QMetaType::typeName(propType)))); + } + } else { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".") + .arg(QLatin1String(QMetaType::typeName(propType)))); + } + + } + return noError; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h new file mode 100644 index 0000000000..74a1281927 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalidator_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYVALIDATOR_P_H +#define QQMLPROPERTYVALIDATOR_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 <private/qqmlengine_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qv4compileddata_p.h> + +#include <QtCore/qcoreapplication.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyValidator +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) +public: + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); + + QVector<QQmlJS::DiagnosticMessage> validate(); + +private: + QVector<QQmlJS::DiagnosticMessage> validateObject( + int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, + bool populatingValueTypeGroupProperty = false) const; + QQmlJS::DiagnosticMessage validateLiteralBinding( + QQmlPropertyCache *propertyCache, QQmlPropertyData *property, + const QV4::CompiledData::Binding *binding) const; + QQmlJS::DiagnosticMessage validateObjectBinding( + QQmlPropertyData *property, const QString &propertyName, + const QV4::CompiledData::Binding *binding) const; + + bool canCoerce(int to, QQmlPropertyCache *fromMo) const; + + Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError( + const QV4::CompiledData::Location &location, const QString &description) const; + Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError( + const QQmlJS::DiagnosticMessage &error) const; + QString stringAt(int index) const { return compilationUnit->stringAt(index); } + QV4::ResolvedTypeReference *resolvedType(int id) const + { + return compilationUnit->resolvedType(id); + } + + QQmlEnginePrivate *enginePrivate; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; + const QQmlImports &imports; + const QV4::CompiledData::Unit *qmlUnit; + const QQmlPropertyCacheVector &propertyCaches; + + QVector<QV4::BindingPropertyData> * const bindingPropertyDataPerObject; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp new file mode 100644 index 0000000000..eb103dc434 --- /dev/null +++ b/src/qml/qml/qqmlscriptblob.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlengine_p.h> +#include <private/qqmlirbuilder_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmlscriptdata_p.h> +#include <private/qv4runtimecodegen_p.h> +#include <private/qv4script_p.h> + +#include <QtCore/qloggingcategory.h> + +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) +Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") + +QT_BEGIN_NAMESPACE + +QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) + : QQmlTypeLoader::Blob(url, JavaScriptFile, loader) + , m_isModule(url.path().endsWith(QLatin1String(".mjs"))) +{ +} + +QQmlScriptBlob::~QQmlScriptBlob() +{ +} + +QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const +{ + return m_scriptData; +} + +void QQmlScriptBlob::dataReceived(const SourceCodeData &data) +{ + if (diskCacheEnabled()) { + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit + = QV4::ExecutableCompilationUnit::create(); + QString error; + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { + initializeFromCompilationUnit(unit); + return; + } else { + qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; + } + } + + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } + + QString error; + QString source = data.readAll(&error); + if (!error.isEmpty()) { + setError(error); + return; + } + + QV4::CompiledData::CompilationUnit unit; + + if (m_isModule) { + QList<QQmlJS::DiagnosticMessage> diagnostics; + unit = QV4::Compiler::Codegen::compileModule(isDebugging(), urlString(), source, + data.sourceTimeStamp(), &diagnostics); + QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics); + if (!errors.isEmpty()) { + setError(errors); + return; + } + } else { + QmlIR::Document irUnit(isDebugging()); + + irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); + + QmlIR::ScriptDirectivesCollector collector(&irUnit); + irUnit.jsParserEngine.setDirectives(&collector); + + QList<QQmlError> errors; + irUnit.javaScriptCompilationUnit = QV4::Script::precompile( + &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), + source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML); + + source.clear(); + if (!errors.isEmpty()) { + setError(errors); + return; + } + + QmlIR::QmlUnitGenerator qmlGenerator; + qmlGenerator.generate(irUnit); + unit = std::move(irUnit.javaScriptCompilationUnit); + } + + auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit)); + + if (diskCacheEnabled()) { + QString errorString; + if (executableUnit->saveToDisk(url(), &errorString)) { + QString error; + if (!executableUnit->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; + } + } + + initializeFromCompilationUnit(executableUnit); +} + +void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) +{ + initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString()))); +} + +void QQmlScriptBlob::done() +{ + if (isError()) + return; + + // Check all script dependencies for errors + for (int ii = 0; ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList<QQmlError> errors = script.script->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); + errors.prepend(error); + setError(errors); + return; + } + } + + if (!m_isModule) { + m_scriptData->typeNameCache.adopt(new QQmlTypeNameCache(m_importCache)); + + QSet<QString> ns; + + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const ScriptReference &script = m_scripts.at(scriptIndex); + + m_scriptData->scripts.append(script.script); + + if (!script.nameSpace.isNull()) { + if (!ns.contains(script.nameSpace)) { + ns.insert(script.nameSpace); + m_scriptData->typeNameCache->add(script.nameSpace); + } + } + m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); + } + + m_importCache.populateCache(m_scriptData->typeNameCache.data()); + } + m_scripts.clear(); +} + +QString QQmlScriptBlob::stringAt(int index) const +{ + return m_scriptData->m_precompiledScript->stringAt(index); +} + +void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) +{ + ScriptReference ref; + ref.script = blob; + ref.location = location; + ref.qualifier = qualifier; + ref.nameSpace = nameSpace; + + m_scripts << ref; +} + +void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit) +{ + Q_ASSERT(!m_scriptData); + m_scriptData.adopt(new QQmlScriptData()); + m_scriptData->url = finalUrl(); + m_scriptData->urlString = finalUrlString(); + m_scriptData->m_precompiledScript = unit; + + 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); + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return; + } + } + } + + auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); + + v4->injectModule(unit); + + for (const QString &request: unit->moduleRequests()) { + if (v4->moduleForUrl(QUrl(request), unit.data())) + 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()); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h new file mode 100644 index 0000000000..10c0437e7b --- /dev/null +++ b/src/qml/qml/qqmlscriptblob_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTBLOB_P_H +#define QQMLSCRIPTBLOB_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 <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlScriptData; +class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob +{ +private: + friend class QQmlTypeLoader; + + QQmlScriptBlob(const QUrl &, QQmlTypeLoader *); + +public: + ~QQmlScriptBlob() override; + + struct ScriptReference + { + QV4::CompiledData::Location location; + QString qualifier; + QString nameSpace; + QQmlRefPointer<QQmlScriptBlob> script; + }; + + QQmlRefPointer<QQmlScriptData> scriptData() const; + +protected: + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; + void done() override; + + QString stringAt(int index) const override; + +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); + + QList<ScriptReference> m_scripts; + QQmlRefPointer<QQmlScriptData> m_scriptData; + const bool m_isModule; +}; + +QT_END_NAMESPACE + +#endif // QQMLSCRIPTBLOB_P_H diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp new file mode 100644 index 0000000000..ae268ca904 --- /dev/null +++ b/src/qml/qml/qqmlscriptdata.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlscriptdata_p.h> +#include <private/qqmlcontext_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4object_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4module_p.h> + +QT_BEGIN_NAMESPACE + +QQmlScriptData::QQmlScriptData() + : typeNameCache(nullptr) + , m_loaded(false) +{ +} + +QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData) +{ + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); + + if (m_precompiledScript->isESModule()) + return nullptr; + + auto qmlContextData = new QQmlContextData(); + + qmlContextData->isInternal = true; + qmlContextData->isJSContext = true; + if (m_precompiledScript->isSharedLibrary()) + qmlContextData->isPragmaLibraryContext = true; + else + qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext; + qmlContextData->baseUrl = url; + qmlContextData->baseUrlString = urlString; + + // For backward compatibility, if there are no imports, we need to use the + // imports from the parent context. See QTBUG-17518. + if (!typeNameCache->isEmpty()) { + qmlContextData->imports = typeNameCache; + } else if (!m_precompiledScript->isSharedLibrary()) { + qmlContextData->imports = parentQmlContextData->imports; + qmlContextData->importedScripts = parentQmlContextData->importedScripts; + } + + if (!m_precompiledScript->isSharedLibrary()) { + qmlContextData->setParent(parentQmlContextData); + } else { + qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620 + } + + QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + QV4::Scope scope(v4); + QV4::ScopedObject scriptsArray(scope); + if (qmlContextData->importedScripts.isNullOrUndefined()) { + scriptsArray = v4->newArrayObject(scripts.count()); + qmlContextData->importedScripts.set(v4, scriptsArray); + } else { + scriptsArray = qmlContextData->importedScripts.valueRef(); + } + QV4::ScopedValue v(scope); + for (int ii = 0; ii < scripts.count(); ++ii) + scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData))); + + return qmlContextData; +} + +QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData) +{ + if (m_loaded) + return m_value.value(); + + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); + QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + QV4::Scope scope(v4); + + if (!hasEngine()) { + addToEngine(parentQmlContextData->engine); + addref(); + } + + QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData); + QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope); + if (qmlContextData) + qmlExecutionContext = + QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr); + + QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4)); + if (module) { + if (qmlContextData) { + module->d()->scope->outer.set(v4, qmlExecutionContext->d()); + qmlExecutionContext->d()->qml()->module.set(v4, module->d()); + } + + module->evaluate(); + } + + if (v4->hasException) { + QQmlError error = v4->catchExceptionAsQmlError(); + if (error.isValid()) + QQmlEnginePrivate::get(v4)->warning(error); + } + + QV4::ScopedValue value(scope); + if (qmlContextData) + value = qmlExecutionContext->d()->qml(); + else if (module) + value = module->d(); + + if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) { + m_loaded = true; + m_value.set(v4, value); + } + + return value->asReturnedValue(); +} + +void QQmlScriptData::clear() +{ + typeNameCache = nullptr; + scripts.clear(); + + // An addref() was made when the QQmlCleanup was added to the engine. + release(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h new file mode 100644 index 0000000000..80b65b699c --- /dev/null +++ b/src/qml/qml/qqmlscriptdata_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTDATA_P_H +#define QQMLSCRIPTDATA_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 <private/qqmlrefcount_p.h> +#include <private/qqmlcleanup_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qv4value_p.h> +#include <private/qv4persistent_p.h> +#include <private/qv4executablecompilationunit_p.h> + +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeNameCache; +class QQmlContextData; + +// QQmlScriptData instances are created, uninitialized, by the loader in the +// load thread. The first time they are used by the VME, they are initialized which +// creates their v8 objects and they are referenced and added to the engine's cleanup +// list. During QQmlCleanup::clear() all v8 resources are destroyed, and the +// reference that was created is released but final deletion only occurs once all the +// references as released. This is all intended to ensure that the v8 resources are +// only created and destroyed in the main thread :) +class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount +{ +private: + friend class QQmlTypeLoader; + + QQmlScriptData(); + +public: + QUrl url; + QString urlString; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; + + QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); + + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; } + +protected: + void clear() override; // From QQmlCleanup + +private: + friend class QQmlScriptBlob; + + void initialize(QQmlEngine *); + QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData); + + bool m_loaded; + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript; + QV4::PersistentValue m_value; +}; + +QT_END_NAMESPACE + +#endif // QQMLSCRIPTDATA_P_H diff --git a/src/qml/qml/qqmlsourcecoordinate_p.h b/src/qml/qml/qqmlsourcecoordinate_p.h new file mode 100644 index 0000000000..55e11fd147 --- /dev/null +++ b/src/qml/qml/qqmlsourcecoordinate_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSOURCECOORDINATE_P_H +#define QQMLSOURCECOORDINATE_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/qglobal.h> + +#include <limits> + +QT_BEGIN_NAMESPACE + +// These methods are needed because in some public methods we historically interpret -1 as the +// invalid line or column, even though all the lines and columns are 1-based. Also, the different +// integer ranges may turn certain large values into invalid ones on conversion. + +template<typename From, typename To> +To qmlConvertSourceCoordinate(From n); + +template<> +inline quint16 qmlConvertSourceCoordinate<int, quint16>(int n) +{ + return (n > 0 && n <= int(std::numeric_limits<quint16>::max())) ? quint16(n) : 0; +} + +template<> +inline quint32 qmlConvertSourceCoordinate<int, quint32>(int n) +{ + return n > 0 ? quint32(n) : 0u; +} + +// TODO: In Qt6, change behavior and make the invalid coordinate 0 for the following two methods. + +template<> +inline int qmlConvertSourceCoordinate<quint16, int>(quint16 n) +{ + return (n == 0u) ? -1 : int(n); +} + +template<> +inline int qmlConvertSourceCoordinate<quint32, int>(quint32 n) +{ + return (n == 0u || n > quint32(std::numeric_limits<int>::max())) ? -1 : int(n); +} + +QT_END_NAMESPACE + +#endif // QQMLSOURCECOORDINATE_P_H diff --git a/src/qml/qml/qqmlstaticmetaobject.cpp b/src/qml/qml/qqmlstaticmetaobject.cpp new file mode 100644 index 0000000000..218d0134fd --- /dev/null +++ b/src/qml/qml/qqmlstaticmetaobject.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlstaticmetaobject_p.h" + +QT_BEGIN_NAMESPACE + +int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, + QByteArray *unknownTypeError) const +{ + QMetaMethod m = _m.asT2()->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlstaticmetaobject_p.h b/src/qml/qml/qqmlstaticmetaobject_p.h new file mode 100644 index 0000000000..e1ca496080 --- /dev/null +++ b/src/qml/qml/qqmlstaticmetaobject_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSTATICMETAOBJECT_P_H +#define QQMLSTATICMETAOBJECT_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 <private/qqmlobjectorgadget_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; +}; + +QT_END_NAMESPACE + +#endif // QQMLSTATICMETAOBJECT_P_H diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp new file mode 100644 index 0000000000..874bcd4bca --- /dev/null +++ b/src/qml/qml/qqmltype.cpp @@ -0,0 +1,885 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltype_p_p.h" + +#include <QtQml/qjsvalue.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> + +#include <private/qqmlcustomparser_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmltypedata_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) + : regType(type), iid(nullptr), typeId(0), listId(0), revision(0), + containsRevisionedAttributes(false), baseMetaObject(nullptr), + index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false), + haveSuperType(false) +{ + switch (type) { + case QQmlType::CppType: + extraData.cd = new QQmlCppTypeData; + extraData.cd->allocationSize = 0; + extraData.cd->newFunc = 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->registerEnumClassesUnscoped = true; + break; + case QQmlType::SingletonType: + case QQmlType::CompositeSingletonType: + extraData.sd = new QQmlSingletonTypeData; + extraData.sd->singletonInstanceInfo = nullptr; + break; + case QQmlType::InterfaceType: + extraData.cd = nullptr; + break; + case QQmlType::CompositeType: + extraData.fd = new QQmlCompositeTypeData; + break; + default: qFatal("QQmlTypePrivate Internal Error."); + } +} + +QQmlTypePrivate::~QQmlTypePrivate() +{ + qDeleteAll(scopedEnums); + for (const auto &metaObject : metaObjects) + free(metaObject.metaObject); + switch (regType) { + case QQmlType::CppType: + delete extraData.cd->customParser; + delete extraData.cd; + break; + case QQmlType::SingletonType: + case QQmlType::CompositeSingletonType: + delete extraData.sd->singletonInstanceInfo; + delete extraData.sd; + break; + case QQmlType::CompositeType: + delete extraData.fd; + break; + default: //Also InterfaceType, because it has no extra data + break; + } +} + +QQmlType::QQmlType() = default; +QQmlType::QQmlType(const QQmlType &) = default; +QQmlType::QQmlType(QQmlType &&) = default; +QQmlType &QQmlType::operator =(const QQmlType &other) = default; +QQmlType &QQmlType::operator =(QQmlType &&other) = default; +QQmlType::QQmlType(const QQmlTypePrivate *priv) : d(priv) {} +QQmlType::~QQmlType() = default; + +QHashedString QQmlType::module() const +{ + if (!d) + return QHashedString(); + return d->module; +} + +int QQmlType::majorVersion() const +{ + if (!d) + return -1; + return d->version_maj; +} + +int QQmlType::minorVersion() const +{ + if (!d) + return -1; + return d->version_min; +} + +bool QQmlType::availableInVersion(int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; + return vmajor == d->version_maj && vminor >= d->version_min; +} + +bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; + return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; +} + +QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +{ + Q_ASSERT(isComposite()); + if (!engine) + return QQmlType(); + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); + if (td.isNull() || !td->isComplete()) + return QQmlType(); + QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit(); + const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); + return QQmlMetaType::qmlType(mo); +} + +QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const +{ + // similar logic to resolveCompositeBaseType + Q_ASSERT(isComposite()); + if (!engine) + return nullptr; + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); + if (td.isNull() || !td->isComplete()) + return nullptr; + QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit(); + return compilationUnit->rootPropertyCache().data(); +} + +static bool isPropertyRevisioned(const QMetaObject *mo, int index) +{ + int i = index; + i -= mo->propertyOffset(); + if (i < 0 && mo->d.superdata) + return isPropertyRevisioned(mo->d.superdata, index); + + const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data); + if (i >= 0 && i < mop->propertyCount) { + int handle = mop->propertyData + 3*i; + int flags = mo->d.data[handle + 2]; + + return (flags & Revisioned); + } + + return false; +} + +void QQmlTypePrivate::init() const +{ + if (isSetup) + return; + + QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); + if (isSetup) + return; + + const QMetaObject *mo = baseMetaObject; + if (!mo) { + // version 0 singleton type without metaobject information + return; + } + + if (regType == QQmlType::CppType) { + // Setup extended meta object + // XXX - very inefficient + if (extraData.cd->extFunc) { + QMetaObjectBuilder builder; + QQmlMetaType::clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, + extraData.cd->extMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = mo; + QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 }; + metaObjects << data; + } + } + + metaObjects.append(QQmlMetaType::proxyData( + mo, baseMetaObject, metaObjects.isEmpty() ? nullptr + : metaObjects.constLast().metaObject)); + + for (int ii = 0; ii < metaObjects.count(); ++ii) { + metaObjects[ii].propertyOffset = + metaObjects.at(ii).metaObject->propertyOffset(); + metaObjects[ii].methodOffset = + metaObjects.at(ii).metaObject->methodOffset(); + } + + // Check for revisioned details + { + const QMetaObject *mo = nullptr; + if (metaObjects.isEmpty()) + mo = baseMetaObject; + else + mo = metaObjects.constFirst().metaObject; + + for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { + if (isPropertyRevisioned(mo, ii)) + containsRevisionedAttributes = true; + } + + for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { + if (mo->method(ii).revision() != 0) + containsRevisionedAttributes = true; + } + } + + isSetup = true; + lock.unlock(); +} + +void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const +{ + const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite()) + ? compositePropertyCache(engine) + : nullptr; + + const QMetaObject *metaObject = !isEnumFromCacheSetup + ? baseMetaObject // beware: It could be a singleton type without metaobject + : nullptr; + + if (!cache && !metaObject) + return; + + init(); + + QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); + + if (cache) { + insertEnumsFromPropertyCache(cache); + isEnumFromCacheSetup = true; + } + + if (metaObject) { + insertEnums(metaObject); + isEnumFromBaseSetup = true; + } +} + +void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const +{ + // Add any enum values defined by 'related' classes + if (metaObject->d.relatedMetaObjects) { + const auto *related = metaObject->d.relatedMetaObjects; + if (related) { + while (*related) + insertEnums(*related++); + } + } + + QSet<QString> localEnums; + const QMetaObject *localMetaObject = nullptr; + + // 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); + const bool isScoped = e.isScoped(); + QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr; + + // We allow enums in sub-classes to overwrite enums from base-classes, such as + // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin). + // This is acceptable because the _use_ of the enum from the QML side requires qualification + // anyway, i.e. ListView.Center vs. Item.Center. + // However if a class defines two enums with the same value, then that must produce a warning + // because it represents a valid conflict. + if (e.enclosingMetaObject() != localMetaObject) { + localEnums.clear(); + localMetaObject = e.enclosingMetaObject(); + } + + 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 (localEnums.contains(key)) { + auto existingEntry = enums.find(key); + if (existingEntry != 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); + } + if (isScoped) + scoped->insert(key, value); + } + + if (isScoped) { + scopedEnums << scoped; + scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1); + } + } +} + +void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const +{ + path.append(QString::fromUtf8(metaObject->className())); + + if (metaObject->d.relatedMetaObjects) { + const auto *related = metaObject->d.relatedMetaObjects; + if (related) { + while (*related) + createListOfPossibleConflictingItems(*related++, enumInfoList, path); + } + } + + for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { + const auto e = metaObject->enumerator(ii); + + for (int jj = 0; jj < e.keyCount(); ++jj) { + const QString key = QString::fromUtf8(e.key(jj)); + + EnumInfo enumInfo; + enumInfo.metaObjectName = QString::fromUtf8(metaObject->className()); + enumInfo.enumName = QString::fromUtf8(e.name()); + enumInfo.enumKey = key; + enumInfo.scoped = e.isScoped(); + enumInfo.path = path; + enumInfo.metaEnumScope = QString::fromUtf8(e.scope()); + enumInfoList.append(enumInfo); + } + } +} + +void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const +{ + QList<EnumInfo> enumInfoList; + + if (baseMetaObject) // prefer baseMetaObject if available + metaObject = baseMetaObject; + + if (!metaObject) { // If there is no metaObject at all return early + qWarning() << "No meta object information available. Skipping conflict analysis."; + return; + } + + createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList()); + + qWarning().noquote() << QLatin1String("Possible conflicting items:"); + // find items with conflicting key + for (const auto &i : qAsConst(enumInfoList)) { + if (i.enumKey == conflictingKey) + qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope " + << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->")); + } +} + +void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const +{ + const QMetaObject *cppMetaObject = cache->firstCppMetaObject(); + + while (cache && cache->metaObject() != cppMetaObject) { + + int count = cache->qmlEnumCount(); + for (int ii = 0; ii < count; ++ii) { + QStringHash<int> *scoped = new QStringHash<int>(); + QQmlEnumData *enumData = cache->qmlEnum(ii); + + for (int jj = 0; jj < enumData->values.count(); ++jj) { + const QQmlEnumValue &value = enumData->values.at(jj); + enums.insert(value.namedValue, value.value); + scoped->insert(value.namedValue, value.value); + } + scopedEnums << scoped; + scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1); + } + cache = cache->parent(); + } + insertEnums(cppMetaObject); +} + +void QQmlTypePrivate::setName(const QString &uri, const QString &element) +{ + module = uri; + elementName = element; + name = uri.isEmpty() ? element : (uri + QLatin1Char('/') + element); +} + +QByteArray QQmlType::typeName() const +{ + if (d) { + if (d->regType == SingletonType || d->regType == CompositeSingletonType) + return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); + else if (d->baseMetaObject) + return d->baseMetaObject->className(); + } + return QByteArray(); +} + +QString QQmlType::elementName() const +{ + if (!d) + return QString(); + return d->elementName; +} + +QString QQmlType::qmlTypeName() const +{ + if (!d) + return QString(); + return d->name; +} + +QObject *QQmlType::create() const +{ + if (!d || !isCreatable()) + return nullptr; + + d->init(); + + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize); + d->extraData.cd->newFunc(rv); + + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); + + return rv; +} + +void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const +{ + if (!d || !isCreatable()) + return; + + d->init(); + + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory); + d->extraData.cd->newFunc(rv); + + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); + + *out = rv; + *memory = ((char *)rv) + d->extraData.cd->allocationSize; +} + +QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const +{ + if (!d) + return nullptr; + if (d->regType != SingletonType && d->regType != CompositeSingletonType) + return nullptr; + return d->extraData.sd->singletonInstanceInfo; +} + +QQmlCustomParser *QQmlType::customParser() const +{ + if (!d) + return nullptr; + if (d->regType != CppType) + return nullptr; + return d->extraData.cd->customParser; +} + +QQmlType::CreateFunc QQmlType::createFunction() const +{ + if (!d || d->regType != CppType) + return nullptr; + return d->extraData.cd->newFunc; +} + +QString QQmlType::noCreationReason() const +{ + if (!d || d->regType != CppType) + return QString(); + return d->extraData.cd->noCreationReason; +} + +bool QQmlType::isCreatable() const +{ + return d && d->regType == CppType && d->extraData.cd->newFunc; +} + +QQmlType::ExtensionFunc QQmlType::extensionFunction() const +{ + if (!d || d->regType != CppType) + return nullptr; + return d->extraData.cd->extFunc; +} + +bool QQmlType::isExtendedType() const +{ + if (!d) + return false; + d->init(); + + return !d->metaObjects.isEmpty(); +} + +bool QQmlType::isSingleton() const +{ + return d && (d->regType == SingletonType || d->regType == CompositeSingletonType); +} + +bool QQmlType::isInterface() const +{ + return d && d->regType == InterfaceType; +} + +bool QQmlType::isComposite() const +{ + return d && d->isComposite(); +} + +bool QQmlType::isCompositeSingleton() const +{ + return d && d->regType == CompositeSingletonType; +} + +bool QQmlType::isQObjectSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback; +} + +bool QQmlType::isQJSValueSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback; +} + +int QQmlType::typeId() const +{ + return d ? d->typeId : -1; +} + +int QQmlType::qListTypeId() const +{ + return d ? d->listId : -1; +} + +const QMetaObject *QQmlType::metaObject() const +{ + if (!d) + return nullptr; + d->init(); + + if (d->metaObjects.isEmpty()) + return d->baseMetaObject; + else + return d->metaObjects.constFirst().metaObject; + +} + +const QMetaObject *QQmlType::baseMetaObject() const +{ + return d ? d->baseMetaObject : nullptr; +} + +bool QQmlType::containsRevisionedAttributes() const +{ + if (!d) + return false; + d->init(); + + return d->containsRevisionedAttributes; +} + +int QQmlType::metaObjectRevision() const +{ + return d ? d->revision : -1; +} + +QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const +{ + if (!d) + return nullptr; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesFunc; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesFunction(engine); +} + +const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const +{ + if (!d) + return nullptr; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesType; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesType(engine); +} + +#if QT_DEPRECATED_SINCE(5, 14) +/* +This is the id passed to qmlAttachedPropertiesById(). This is different from the index +for the case that a single class is registered under two or more names (eg. Item in +Qt 4.7 and QtQuick 1.0). +*/ +int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const +{ + if (!d) + return -1; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesType ? d->index : -1; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesId(engine); +} +#endif + +int QQmlType::parserStatusCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->parserStatusCast; +} + +int QQmlType::propertyValueSourceCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueSourceCast; +} + +int QQmlType::propertyValueInterceptorCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueInterceptorCast; +} + +const char *QQmlType::interfaceIId() const +{ + if (!d || d->regType != InterfaceType) + return nullptr; + return d->iid; +} + +int QQmlType::index() const +{ + return d ? d->index : -1; +} + +QUrl QQmlType::sourceUrl() const +{ + 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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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.count()); + int *rv = d->scopedEnums.at(index)->value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +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.count()); + int *rv = d->scopedEnums.at(index)->value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); + if (rv) { + int index = *rv; + Q_ASSERT(index > -1 && index < d->scopedEnums.count()); + rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length())); + if (rv) + return *rv; + } + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &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.count()); + rv = d->scopedEnums.at(index)->value(QHashedStringRef(name)); + if (rv) + return *rv; + } + } + + *ok = false; + return -1; +} + +void QQmlType::refHandle(const QQmlTypePrivate *priv) +{ + if (priv) + priv->addref(); +} + +void QQmlType::derefHandle(const QQmlTypePrivate *priv) +{ + if (priv) + priv->release(); +} + +int QQmlType::refCount(const QQmlTypePrivate *priv) +{ + if (priv) + return priv->count(); + return -1; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h new file mode 100644 index 0000000000..ec27b38a73 --- /dev/null +++ b/src/qml/qml/qqmltype_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPE_P_H +#define QQMLTYPE_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 <functional> + +#include <private/qtqmlglobal_p.h> +#include <private/qqmlrefcount_p.h> + +#include <QtQml/qqmlprivate.h> +#include <QtQml/qjsvalue.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QHashedCStringRef; +class QQmlTypePrivate; +class QHashedString; +class QHashedStringRef; +class QQmlCustomParser; +class QQmlEnginePrivate; +class QQmlPropertyCache; + +namespace QV4 { +struct String; +} + +class Q_QML_PRIVATE_EXPORT QQmlType +{ +public: + QQmlType(); + QQmlType(const QQmlType &other); + QQmlType(QQmlType &&other); + QQmlType &operator =(const QQmlType &other); + QQmlType &operator =(QQmlType &&other); + 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; + QString qmlTypeName() const; + QString elementName() const; + + QHashedString module() const; + int majorVersion() const; + int minorVersion() const; + + bool availableInVersion(int vmajor, int vminor) const; + bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const; + + QObject *create() const; + void create(QObject **, void **, size_t) const; + + typedef void (*CreateFunc)(void *); + CreateFunc createFunction() const; + QQmlCustomParser *customParser() const; + + bool isCreatable() const; + typedef QObject *(*ExtensionFunc)(QObject *); + ExtensionFunc extensionFunction() const; + bool isExtendedType() const; + QString noCreationReason() const; + + bool isSingleton() const; + bool isInterface() const; + bool isComposite() const; + bool isCompositeSingleton() const; + bool isQObjectSingleton() const; + bool isQJSValueSingleton() const; + + int typeId() const; + int qListTypeId() const; + + const QMetaObject *metaObject() const; + const QMetaObject *baseMetaObject() const; + int metaObjectRevision() const; + bool containsRevisionedAttributes() const; + + QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; + const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED int attachedPropertiesId(QQmlEnginePrivate *engine) const; +#endif + + int parserStatusCast() const; + const char *interfaceIId() const; + int propertyValueSourceCast() const; + int propertyValueInterceptorCast() const; + + int index() const; + + struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo + { + QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr; + std::function<QObject *(QQmlEngine *, QJSEngine *)> qobjectCallback = {}; + const QMetaObject *instanceMetaObject = nullptr; + QString typeName; + QUrl url; // used by composite singletons + }; + SingletonInstanceInfo *singletonInstanceInfo() const; + + QUrl sourceUrl() const; + + int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; + + int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; + 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, const QStringRef &, const QStringRef &, bool *ok) const; + + const QQmlTypePrivate *priv() const { return d.data(); } + static void refHandle(const QQmlTypePrivate *priv); + static void derefHandle(const QQmlTypePrivate *priv); + static int refCount(const QQmlTypePrivate *priv); + + enum RegistrationType { + CppType = 0, + SingletonType = 1, + InterfaceType = 2, + CompositeType = 3, + CompositeSingletonType = 4, + AnyRegistrationType = 255 + }; + +private: + friend uint qHash(const QQmlType &t, uint seed); + QQmlRefPointer<const QQmlTypePrivate> d; +}; + +inline uint qHash(const QQmlType &t, uint seed = 0) +{ + return qHash(reinterpret_cast<quintptr>(t.d.data()), seed); +} + +QT_END_NAMESPACE + +#endif // QQMLTYPE_P_H diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h new file mode 100644 index 0000000000..6a2d961de8 --- /dev/null +++ b/src/qml/qml/qqmltype_p_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPE_P_P_H +#define QQMLTYPE_P_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 <private/qqmltype_p.h> +#include <private/qstringhash_p.h> +#include <private/qqmlproxymetaobject_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypePrivate : public QQmlRefCount +{ + Q_DISABLE_COPY_MOVE(QQmlTypePrivate) +public: + QQmlTypePrivate(QQmlType::RegistrationType type); + + void init() const; + void initEnums(QQmlEnginePrivate *engine) const; + void insertEnums(const QMetaObject *metaObject) const; + void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; + + QUrl sourceUrl() const + { + switch (regType) { + case QQmlType::CompositeType: + return extraData.fd->url; + case QQmlType::CompositeSingletonType: + return extraData.sd->singletonInstanceInfo->url; + default: + return QUrl(); + } + } + + bool isComposite() const + { + return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType; + } + + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; + + QQmlType::RegistrationType regType; + + struct QQmlCppTypeData + { + int allocationSize; + void (*newFunc)(void *); + QString noCreationReason; + int parserStatusCast; + QObject *(*extFunc)(QObject *); + const QMetaObject *extMetaObject; + QQmlCustomParser *customParser; + QQmlAttachedPropertiesFunc attachedPropertiesFunc; + const QMetaObject *attachedPropertiesType; + int propertyValueSourceCast; + int propertyValueInterceptorCast; + bool registerEnumClassesUnscoped; + }; + + struct QQmlSingletonTypeData + { + QQmlType::SingletonInstanceInfo *singletonInstanceInfo; + }; + + struct QQmlCompositeTypeData + { + QUrl url; + }; + + union extraData { + QQmlCppTypeData* cd; + QQmlSingletonTypeData* sd; + QQmlCompositeTypeData* fd; + } extraData; + + const char *iid; + QHashedString module; + QString name; + QString elementName; + int version_maj; + int version_min; + int typeId; + int listId; + int revision; + mutable bool containsRevisionedAttributes; + mutable QQmlType superType; + const QMetaObject *baseMetaObject; + + int index; + mutable volatile bool isSetup:1; + mutable volatile bool isEnumFromCacheSetup:1; + mutable volatile bool isEnumFromBaseSetup:1; + mutable bool haveSuperType:1; + 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; + + void setName(const QString &uri, const QString &element); + +private: + ~QQmlTypePrivate() override; + + struct EnumInfo { + QStringList path; + QString metaObjectName; + QString enumName; + QString enumKey; + QString metaEnumScope; + bool scoped; + }; + + void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const; + void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPE_P_P_H diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp new file mode 100644 index 0000000000..9ff0e3fb9e --- /dev/null +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -0,0 +1,1375 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypecompiler_p.h" + +#include <private/qqmlobjectcreator_p.h> +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlcomponent_p.h> +#include <private/qqmlpropertyresolver_p.h> + +#define COMPILE_EXCEPTION(token, desc) \ + { \ + recordError((token)->location, desc); \ + return false; \ + } + +QT_BEGIN_NAMESPACE + +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, + QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) + : resolvedTypes(resolvedTypeCache) + , engine(engine) + , typeData(typeData) + , dependencyHasher(dependencyHasher) + , typeNameCache(typeNameCache) + , document(parsedQML) +{ +} + +QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() +{ + // Build property caches and VME meta object data + + for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd(); + it != end; ++it) { + QQmlCustomParser *customParser = (*it)->type.customParser(); + if (customParser) + customParsers.insert(it.key(), customParser); + } + + QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; + + + { + QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, + engine, this, imports()); + QQmlJS::DiagnosticMessage error = propertyCacheBuilder.buildMetaObjects(); + if (error.isValid()) { + recordError(error); + return nullptr; + } + } + + { + QQmlDefaultPropertyMerger merger(this); + merger.mergeDefaultProperties(); + } + + { + SignalHandlerConverter converter(this); + if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) + return nullptr; + } + + { + QQmlEnumTypeResolver enumResolver(this); + if (!enumResolver.resolveEnumBindings()) + return nullptr; + } + + { + QQmlCustomParserScriptIndexer cpi(this); + cpi.annotateBindingsWithScriptStrings(); + } + + { + QQmlAliasAnnotator annotator(this); + annotator.annotateBindingsToAliases(); + } + + // Resolve component boundaries and aliases + + { + // Scan for components, determine their scopes and resolve aliases within the scope. + QQmlComponentAndAliasResolver resolver(this); + if (!resolver.resolve()) + return nullptr; + + pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches); + } + + { + QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); + if (!deferredAndCustomParserBindingScanner.scanObject()) + return nullptr; + } + + if (!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 + // without type optimizations as their scope is always entirely dynamic. + QQmlScriptStringScanner sss(this); + sss.scan(); + } + + document->jsModule.fileName = typeData->urlString(); + document->jsModule.finalUrl = typeData->finalUrlString(); + QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames()); + if (!v4CodeGenerator.generateCodeForComponents(componentRoots())) { + recordError(v4CodeGenerator.error()); + return nullptr; + } + + document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); + } + + // Generate QML compiled type data structures + + QmlIR::QmlUnitGenerator qmlGenerator; + qmlGenerator.generate(*document, dependencyHasher); + + 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; +} + +void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + error.setUrl(url()); + errors << error; +} + +void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message) +{ + QQmlError error; + error.setDescription(message.message); + error.setLine(message.line); + error.setColumn(message.column); + error.setUrl(url()); + errors << error; +} + +QString QQmlTypeCompiler::stringAt(int idx) const +{ + return document->stringAt(idx); +} + +int QQmlTypeCompiler::registerString(const QString &str) +{ + return document->jsGenerator.registerString(str); +} + +int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) +{ + return document->jsGenerator.registerConstant(v); +} + +const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const +{ + return document->javaScriptCompilationUnit.unitData(); +} + +const QQmlImports *QQmlTypeCompiler::imports() const +{ + return &typeData->imports(); +} + +QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const +{ + return &document->objects; +} + +void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) +{ + m_propertyCaches = std::move(caches); + Q_ASSERT(m_propertyCaches.count() > 0); +} + +const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const +{ + return &m_propertyCaches; +} + +QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() +{ + return std::move(m_propertyCaches); +} + +QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() +{ + return document->jsParserEngine.pool(); +} + +QStringRef QQmlTypeCompiler::newStringRef(const QString &string) +{ + return document->jsParserEngine.newStringRef(string); +} + +const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const +{ + return &document->jsGenerator.stringTable; +} + +QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const +{ + return object->bindingAsString(document, scriptIndex); +} + +void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion) +{ + const quint32 moduleIdx = registerString(module); + const quint32 qualifierIdx = registerString(qualifier); + + for (int i = 0, count = document->imports.count(); i < count; ++i) { + const QV4::CompiledData::Import *existingImport = document->imports.at(i); + if (existingImport->type == QV4::CompiledData::Import::ImportLibrary + && existingImport->uriIndex == moduleIdx + && existingImport->qualifierIndex == qualifierIdx) + return; + } + auto pool = memoryPool(); + QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>(); + import->type = QV4::CompiledData::Import::ImportLibrary; + import->majorVersion = majorVersion; + import->minorVersion = minorVersion; + import->uriIndex = moduleIdx; + import->qualifierIndex = qualifierIdx; + document->imports.append(import); +} + +QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) + : compiler(typeCompiler) +{ +} + + + +SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , qmlObjects(*typeCompiler->qmlObjects()) + , imports(typeCompiler->imports()) + , customParsers(typeCompiler->customParserCache()) + , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames()) + , propertyCaches(typeCompiler->propertyCaches()) +{ +} + +bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() +{ + for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { + const QmlIR::Object * const obj = qmlObjects.at(objectIndex); + QQmlPropertyCache *cache = propertyCaches->at(objectIndex); + if (!cache) + continue; + if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { + if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) + continue; + } + const QString elementName = stringAt(obj->inheritedTypeNameIndex); + if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) + return false; + } + return true; +} + +bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache) +{ + // map from signal name defined in qml itself to list of parameters + QHash<QString, QStringList> customSignals; + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + QString propertyName = stringAt(binding->propertyNameIndex); + // Attached property? + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + 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(propertyName, &type, nullptr, nullptr, nullptr); + + const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); + if (!attachedType) + COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); + QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); + if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache)) + return false; + continue; + } + + if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName)) + continue; + + QQmlPropertyResolver resolver(propertyCache); + + Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); + propertyName.remove(0, 2); + + // Note that the property name could start with any alpha or '_' or '$' character, + // so we need to do the lower-casing of the first alpha character. + for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) { + if (propertyName.at(firstAlphaIndex).isUpper()) { + propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower(); + break; + } + } + + QList<QString> parameters; + + bool notInRevision = false; + QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); + if (signal) { + int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); + sigIndex = propertyCache->originalClone(sigIndex); + + bool unnamedParameter = false; + + QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); + for (int i = 0; i < parameterNames.count(); ++i) { + const QString param = QString::fromUtf8(parameterNames.at(i)); + if (param.isEmpty()) + unnamedParameter = true; + else if (unnamedParameter) { + COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter.")); + } else if (illegalNames.contains(param)) { + COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param)); + } + parameters += param; + } + } else { + if (notInRevision) { + // Try assinging it as a property later + if (resolver.property(propertyName, /*notInRevision ptr*/nullptr)) + continue; + + const QString &originalPropertyName = stringAt(binding->propertyNameIndex); + + auto *typeRef = resolvedType(obj->inheritedTypeNameIndex); + const QQmlType type = typeRef ? typeRef->type : QQmlType(); + if (type.isValid()) { + COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion())); + } else { + COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName)); + } + } + + // Try to look up the signal parameter names in the object itself + + // build cache if necessary + if (customSignals.isEmpty()) { + for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) { + const QString &signalName = stringAt(signal->nameIndex); + customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool())); + } + + for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) { + const QString propName = stringAt(property->nameIndex); + customSignals.insert(propName, QStringList()); + } + } + + QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); + if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { + QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); + entry = customSignals.constFind(alternateName); + } + + if (entry == customSignals.constEnd()) { + // Can't find even a custom signal, then just don't do anything and try + // keeping the binding as a regular property assignment. + continue; + } + + parameters = entry.value(); + } + + // Binding object to signal means connect the signal to the object's default method. + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; + continue; + } + + if (binding->type != QV4::CompiledData::Binding::Type_Script) { + if (binding->type < QV4::CompiledData::Binding::Type_Script) { + COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)")); + } else { + COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment")); + } + } + + QQmlJS::MemoryPool *pool = compiler->memoryPool(); + + QQmlJS::AST::FormalParameterList *paramList = nullptr; + for (const QString ¶m : qAsConst(parameters)) { + QStringRef paramNameRef = compiler->newStringRef(param); + + QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr); + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b); + } + + if (paramList) + paramList = paramList->finish(pool); + + QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); + QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr; + if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) { + if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) { + functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body); + functionDeclaration->functionToken = fe->functionToken; + functionDeclaration->identifierToken = fe->identifierToken; + functionDeclaration->lparenToken = fe->lparenToken; + functionDeclaration->rparenToken = fe->rparenToken; + functionDeclaration->lbraceToken = fe->lbraceToken; + functionDeclaration->rbraceToken = fe->rbraceToken; + } + } + if (!functionDeclaration) { + QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); + QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement); + body = body->finish(); + + functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); + functionDeclaration->lbraceToken = functionDeclaration->functionToken + = foe->node->firstSourceLocation(); + functionDeclaration->rbraceToken = foe->node->lastSourceLocation(); + } + foe->node = functionDeclaration; + binding->propertyNameIndex = compiler->registerString(propertyName); + binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; + } + return true; +} + +QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) + , imports(typeCompiler->imports()) +{ +} + +bool QQmlEnumTypeResolver::resolveEnumBindings() +{ + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches->at(i); + if (!propertyCache) + continue; + const QmlIR::Object *obj = qmlObjects.at(i); + + QQmlPropertyResolver resolver(propertyCache); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + const QString propertyName = stringAt(binding->propertyNameIndex); + bool notInRevision = false; + QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); + if (!pd) + continue; + + if (!pd->isEnum() && pd->propType() != QMetaType::Int) + continue; + + if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) + return false; + } + } + + return true; +} + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &staticQtMetaObject; } +}; + +bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject) +{ + if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) { + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); + } + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue)); +// binding->setNumberValueInternal((double)enumValue); + binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + return true; +} + +bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) +{ + bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum(); + if (!prop->isEnum() && !isIntProp) + return true; + + if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); + + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); + if (!string.constData()->isUpper()) + return true; + + // we support one or two '.' in the enum phrase: + // * <TypeName>.<EnumValue> + // * <TypeName>.<ScopedEnumName>.<EnumValue> + + int dot = string.indexOf(QLatin1Char('.')); + if (dot == -1 || dot == string.length()-1) + return true; + + int dot2 = string.indexOf(QLatin1Char('.'), dot+1); + if (dot2 != -1 && dot2 != string.length()-1) { + if (!string.at(dot+1).isUpper()) + return true; + if (string.indexOf(QLatin1Char('.'), dot2+1) != -1) + return true; + } + + QHashedStringRef typeName(string.constData(), dot); + const bool isQtObject = (typeName == QLatin1String("Qt")); + const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef()); + // ### consider supporting scoped enums in Qt namespace + const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1); + + if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here? + // Allow enum assignment to ints. + bool ok; + int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok); + if (ok) { + if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject)) + return false; + } + return true; + } + QQmlType type; + imports->resolveType(typeName, &type, nullptr, nullptr, nullptr); + + if (!type.isValid() && !isQtObject) + return true; + + int value = 0; + bool ok = false; + + auto *tr = resolvedType(obj->inheritedTypeNameIndex); + if (type.isValid() && tr && tr->type == type) { + // When these two match, we can short cut the search + QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); + QMetaEnum menum = mprop.enumerator(); + QByteArray enumName = enumValue.toUtf8(); + if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8()) + return true; + + if (mprop.isFlagType()) { + value = menum.keysToValue(enumName.constData(), &ok); + } else { + value = menum.keyToValue(enumName.constData(), &ok); + } + } else { + // Otherwise we have to search the whole type + if (type.isValid()) { + if (!scopedEnumName.isEmpty()) + value = type.scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok); + else + value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); + } else { + QByteArray enumName = enumValue.toUtf8(); + const QMetaObject *metaObject = StaticQtMetaObject::get(); + for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + value = e.keyToValue(enumName.constData(), &ok); + } + } + } + + if (!ok) + return true; + + return assignEnumToBinding(binding, enumValue, value, isQtObject); +} + +int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const +{ + Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer"); + *ok = false; + + if (scope != QLatin1String("Qt")) { + QQmlType type; + imports->resolveType(scope, &type, nullptr, nullptr, nullptr); + if (!type.isValid()) + return -1; + if (!enumName.isEmpty()) + return type.scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok); + return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok); + } + + const QMetaObject *mo = StaticQtMetaObject::get(); + int i = mo->enumeratorCount(); + const QByteArray ba = enumValue.toUtf8(); + while (i--) { + int v = mo->enumerator(i).keyToValue(ba.constData(), ok); + if (*ok) + return v; + } + return -1; +} + +QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , customParsers(typeCompiler->customParserCache()) +{ +} + +void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings() +{ + scanObjectRecursively(/*root object*/0); +} + +void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings) +{ + const QmlIR::Object * const obj = qmlObjects.at(objectIndex); + if (!annotateScriptBindings) + annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex); + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings); + continue; + } else if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + if (!annotateScriptBindings) + continue; + const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); + binding->stringIndex = compiler->registerString(script); + } +} + +QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) +{ +} + +void QQmlAliasAnnotator::annotateBindingsToAliases() +{ + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches->at(i); + if (!propertyCache) + continue; + + const QmlIR::Object *obj = qmlObjects.at(i); + + QQmlPropertyResolver resolver(propertyCache); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (!binding->isValueBinding()) + continue; + bool notInRevision = false; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + if (pd && pd->isAlias()) + binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; + } + } +} + +QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) +{ + +} + +void QQmlScriptStringScanner::scan() +{ + const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>(); + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches->at(i); + if (!propertyCache) + continue; + + const QmlIR::Object *obj = qmlObjects.at(i); + + QQmlPropertyResolver resolver(propertyCache); + 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_Script) + continue; + bool notInRevision = false; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + if (!pd || pd->propType() != scriptStringMetaType) + continue; + + QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); + binding->stringIndex = compiler->registerString(script); + } + } +} + +QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , pool(typeCompiler->memoryPool()) + , qmlObjects(typeCompiler->qmlObjects()) + , propertyCaches(std::move(typeCompiler->takePropertyCaches())) +{ +} + +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; +} + +void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) +{ + QQmlPropertyResolver propertyResolver(propertyCache); + + 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->flags & 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; + if (tr->type.isValid()) + firstMetaObject = tr->type.metaObject(); + else if (tr->compilationUnit) + firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject(); + if (isUsableComponent(firstMetaObject)) + continue; + // if here, not a QQmlComponent, so needs wrapping + + QQmlPropertyData *pd = nullptr; + if (binding->propertyNameIndex != quint32(0)) { + bool notInRevision = false; + pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); + } else { + pd = defaultProperty; + } + if (!pd || !pd->isQObject()) + continue; + + QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion()); + const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr; + while (mo) { + if (mo == &QQmlComponent::staticMetaObject) + break; + mo = mo->superClass(); + } + + if (!mo) + continue; + + // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" + QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + Q_ASSERT(componentType.isValid()); + const QString qualifier = QStringLiteral("QmlInternals"); + + compiler->addImport(componentType.module(), qualifier, componentType.majorVersion(), componentType.minorVersion()); + + QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); + syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString())); + syntheticComponent->location = binding->valueLocation; + syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; + + if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { + auto typeRef = new QV4::ResolvedTypeReference; + typeRef->type = componentType; + typeRef->majorVersion = componentType.majorVersion(); + typeRef->minorVersion = componentType.minorVersion(); + insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef); + } + + qmlObjects->append(syntheticComponent); + const int componentIndex = qmlObjects->count() - 1; + // Keep property caches symmetric + QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); + propertyCaches.append(componentCache); + + QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); + *syntheticBinding = *binding; + syntheticBinding->type = 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); + } +} + +bool QQmlComponentAndAliasResolver::resolve() +{ + // 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->count(); + for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { + QmlIR::Object *obj = qmlObjects->at(i); + QQmlPropertyCache *cache = propertyCaches.at(i); + 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) + findAndRegisterImplicitComponents(obj, cache); + continue; + } + + obj->flags |= QV4::CompiledData::Object::IsComponent; + + 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); + + } + + for (int i = 0; i < componentRoots.count(); ++i) { + QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); + const QmlIR::Binding *rootBinding = component->firstBinding(); + + _idToObjectIndex.clear(); + + _objectsWithAliases.clear(); + + if (!collectIdsAndAliases(rootBinding->value.objectIndex)) + return false; + + component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + + if (!resolveAliases(componentRoots.at(i))) + return false; + } + + // Collect ids and aliases for root + _idToObjectIndex.clear(); + _objectsWithAliases.clear(); + + collectIdsAndAliases(/*root object*/0); + + QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0); + rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + + if (!resolveAliases(/*root object*/0)) + 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); + + return true; +} + +bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) +{ + 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.count(); + _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) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + if (!collectIdsAndAliases(binding->value.objectIndex)) + return false; + } + + return true; +} + +bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) +{ + if (_objectsWithAliases.isEmpty()) + return true; + + QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler); + + bool atLeastOneAliasResolved; + do { + atLeastOneAliasResolved = false; + QVector<int> pendingObjects; + + for (int objectIndex: qAsConst(_objectsWithAliases)) { + + QQmlJS::DiagnosticMessage error; + const auto result = resolveAliasesInObject(objectIndex, &error); + + if (error.isValid()) { + recordError(error); + return false; + } + + if (result == AllAliasesResolved) { + QQmlJS::DiagnosticMessage 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->flags & QV4::CompiledData::Alias::Resolved)) { + recordError(alias->location, tr("Circular alias reference detected")); + return false; + } + } + } + + return true; +} + +QQmlComponentAndAliasResolver::AliasResolutionResult +QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, + QQmlJS::DiagnosticMessage *error) +{ + const QmlIR::Object * const obj = qmlObjects->at(objectIndex); + if (!obj->aliasCount()) + return AllAliasesResolved; + + int numResolvedAliases = 0; + bool seenUnresolvedAlias = false; + + for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { + if (alias->flags & QV4::CompiledData::Alias::Resolved) + continue; + + seenUnresolvedAlias = true; + + const int idIndex = alias->idIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + break; + } + + const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + Q_ASSERT(targetObject->id >= 0); + alias->targetObjectId = targetObject->id; + alias->aliasToLocalAlias = false; + + const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); + + QStringRef property; + QStringRef subProperty; + + const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); + if (propertySeparator != -1) { + property = aliasPropertyValue.leftRef(propertySeparator); + subProperty = aliasPropertyValue.midRef(propertySeparator + 1); + } else + property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); + + QQmlPropertyIndex propIdx; + + if (property.isEmpty()) { + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } else { + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); + if (!targetCache) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(property.toString())); + break; + } + + QQmlPropertyResolver resolver(targetCache); + + QQmlPropertyData *targetProperty = resolver.property(property.toString()); + + // If it's an alias that we haven't resolved yet, try again later. + if (!targetProperty) { + bool aliasPointsToOtherAlias = false; + int localAliasIndex = 0; + for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { + if (stringAt(targetAlias->nameIndex) == property) { + aliasPointsToOtherAlias = true; + break; + } + } + if (aliasPointsToOtherAlias) { + if (targetObjectIndex == objectIndex) { + alias->localAliasIndex = localAliasIndex; + alias->aliasToLocalAlias = true; + alias->flags |= QV4::CompiledData::Alias::Resolved; + ++numResolvedAliases; + continue; + } + + // restore + alias->idIndex = idIndex; + // Try again later and resolve the target alias first. + break; + } + } + + if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(property.toString())); + break; + } + + propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); + + if (!subProperty.isEmpty()) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); + if (!valueTypeMetaObject) { + // could be a deep alias + bool isDeepAlias = subProperty.at(0).isLower(); + if (isDeepAlias) { + 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)); + QQmlPropertyData *actualProperty = resolver.property(subProperty.toString()); + if (actualProperty) { + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex()); + isDeepAlias = true; + } + } + } + } + if (!isDeepAlias) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + } else { + + int valueTypeIndex = + valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); + } + } else { + if (targetProperty->isQObject()) + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } + } + + alias->encodedMetaPropertyIndex = propIdx.toEncoded(); + alias->flags |= QV4::CompiledData::Alias::Resolved; + numResolvedAliases++; + } + + if (numResolvedAliases == 0) + return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved; + + return SomeAliasesResolved; +} + +QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) + , customParsers(typeCompiler->customParserCache()) + , _seenObjectWithId(false) +{ +} + +bool QQmlDeferredAndCustomParserBindingScanner::scanObject() +{ + return scanObject(/*root object*/0); +} + +bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) +{ + QmlIR::Object *obj = qmlObjects->at(objectIndex); + if (obj->idNameIndex != 0) + _seenObjectWithId = true; + + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + return scanObject(componentBinding->value.objectIndex); + } + + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + if (!propertyCache) + return true; + + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = nullptr; + if (obj->indexOfDefaultPropertyOrAlias != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } + + QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); + + QQmlPropertyResolver propertyResolver(propertyCache); + + QStringList deferredPropertyNames; + { + const QMetaObject *mo = propertyCache->firstCppMetaObject(); + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + } + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + QQmlPropertyData *pd = nullptr; + QString name = stringAt(binding->propertyNameIndex); + + if (customParser) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + continue; + } + } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + continue; + } + } + + if (name.isEmpty()) { + pd = defaultProperty; + name = defaultPropertyName; + } else { + if (name.constData()->isUpper()) + continue; + + bool notInRevision = false; + pd = propertyResolver.property(name, ¬InRevision, + QQmlPropertyResolver::CheckRevision); + } + + bool seenSubObjectWithId = false; + + if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + qSwap(_seenObjectWithId, seenSubObjectWithId); + const bool subObjectValid = scanObject(binding->value.objectIndex); + qSwap(_seenObjectWithId, seenSubObjectWithId); + if (!subObjectValid) + return false; + _seenObjectWithId |= seenSubObjectWithId; + } + + if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty + && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { + + binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; + obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; + } + + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (!pd) { + if (customParser) { + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + } + } + } + + return true; +} + +QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) +{ + +} + +void QQmlDefaultPropertyMerger::mergeDefaultProperties() +{ + for (int i = 0; i < qmlObjects.count(); ++i) + mergeDefaultProperties(i); +} + +void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) +{ + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + if (!propertyCache) + return; + + QmlIR::Object *object = qmlObjects.at(objectIndex); + + QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName(); + QmlIR::Binding *bindingsToReinsert = nullptr; + QmlIR::Binding *tail = nullptr; + + QmlIR::Binding *previousBinding = nullptr; + QmlIR::Binding *binding = object->firstBinding(); + while (binding) { + if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) { + previousBinding = binding; + binding = binding->next; + continue; + } + + QmlIR::Binding *toReinsert = binding; + binding = object->unlinkBinding(previousBinding, binding); + + if (!tail) { + bindingsToReinsert = toReinsert; + tail = toReinsert; + } else { + tail->next = toReinsert; + tail = tail->next; + } + tail->next = nullptr; + } + + binding = bindingsToReinsert; + while (binding) { + QmlIR::Binding *toReinsert = binding; + binding = binding->next; + object->insertSorted(toReinsert); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h new file mode 100644 index 0000000000..40b0337848 --- /dev/null +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLTYPECOMPILER_P_H +#define QQMLTYPECOMPILER_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 <qglobal.h> +#include <qqmlerror.h> +#include <qhash.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmlirbuilder_p.h> +#include <private/qqmlpropertycachecreator_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlEnginePrivate; +class QQmlError; +class QQmlTypeData; +class QQmlImports; + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace CompiledData { +struct QmlUnit; +struct Location; +} +} + +struct QQmlTypeCompiler +{ + Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) +public: + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, + const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher); + + // --- interface used by QQmlPropertyCacheCreator + typedef QmlIR::Object CompiledObject; + const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); } + int objectCount() const { return document->objects.count(); } + 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; + // --- + + QQmlRefPointer<QV4::ExecutableCompilationUnit> compile(); + + QList<QQmlError> compilationErrors() const { return errors; } + void recordError(const QV4::CompiledData::Location &location, const QString &description); + void recordError(const QQmlJS::DiagnosticMessage &error); + + int registerString(const QString &str); + int registerConstant(QV4::ReturnedValue v); + + const QV4::CompiledData::Unit *qmlUnit() const; + + QUrl url() const { return typeData->finalUrl(); } + QQmlEnginePrivate *enginePrivate() const { return engine; } + const QQmlImports *imports() const; + QVector<QmlIR::Object *> *qmlObjects() const; + void setPropertyCaches(QQmlPropertyCacheVector &&caches); + 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(); + QStringRef newStringRef(const QString &string); + const QV4::Compiler::StringTableGenerator *stringPool() const; + + const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } + + QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const; + + void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); + + QV4::ResolvedTypeReference *resolvedType(int id) const + { + return resolvedTypes->value(id); + } + +private: + QList<QQmlError> errors; + QQmlEnginePrivate *engine; + QQmlTypeData *typeData; + const QV4::CompiledData::DependentTypesHasher &dependencyHasher; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QmlIR::Document *document; + // index is string index of type name (use obj->inheritedTypeNameIndex) + 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; +}; + +struct QQmlCompilePass +{ + QQmlCompilePass(QQmlTypeCompiler *typeCompiler); + + QString stringAt(int idx) const { return compiler->stringAt(idx); } +protected: + void recordError(const QV4::CompiledData::Location &location, const QString &description) const + { compiler->recordError(location, description); } + void recordError(const QQmlJS::DiagnosticMessage &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; +}; + +// "Converts" signal expressions to full-fleged function declarations with +// parameters taken from the signal declarations +// It also updates the QV4::CompiledData::Binding objects to set the property name +// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag. +struct SignalHandlerConverter : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(SignalHandlerConverter) +public: + SignalHandlerConverter(QQmlTypeCompiler *typeCompiler); + + bool convertSignalHandlerExpressionsToFunctionDeclarations(); + +private: + bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); + + QQmlEnginePrivate *enginePrivate; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlImports *imports; + const QHash<int, QQmlCustomParser*> &customParsers; + const QSet<QString> &illegalNames; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +// ### This will go away when the codegen resolves all enums to constant expressions +// and we replace the constant expression with a literal binding instead of using +// a script. +class QQmlEnumTypeResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver) +public: + QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler); + + bool resolveEnumBindings(); + +private: + bool assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject); + bool assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject) + { + return assignEnumToBinding(binding, QStringRef(&enumName), enumValue, isQtObject); + } + bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, + const QQmlPropertyData *prop, + QmlIR::Binding *binding); + int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const; + + + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; + const QQmlImports *imports; +}; + +class QQmlCustomParserScriptIndexer: public QQmlCompilePass +{ +public: + QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler); + + void annotateBindingsWithScriptStrings(); + +private: + void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false); + + const QVector<QmlIR::Object*> &qmlObjects; + const QHash<int, QQmlCustomParser*> &customParsers; +}; + +// Annotate properties bound to aliases with a flag +class QQmlAliasAnnotator : public QQmlCompilePass +{ +public: + QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); + + void annotateBindingsToAliases(); +private: + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +class QQmlScriptStringScanner : public QQmlCompilePass +{ +public: + QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); + + void scan(); + +private: + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +class QQmlComponentAndAliasResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) +public: + QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler); + + bool resolve(); + +protected: + void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *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, QQmlJS::DiagnosticMessage *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 +{ +public: + QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); + + bool scanObject(); + +private: + bool scanObject(int objectIndex); + + QVector<QmlIR::Object*> *qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; + const QHash<int, QQmlCustomParser*> &customParsers; + + bool _seenObjectWithId; +}; + +class QQmlDefaultPropertyMerger : public QQmlCompilePass +{ +public: + QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler); + + void mergeDefaultProperties(); + +private: + void mergeDefaultProperties(int objectIndex); + + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp new file mode 100644 index 0000000000..0fd5bc83e6 --- /dev/null +++ b/src/qml/qml/qqmltypedata.cpp @@ -0,0 +1,842 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypedata_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/qqmlscriptblob_p.h> +#include <private/qqmlscriptdata_p.h> +#include <private/qqmltypecompiler_p.h> + +#include <QtCore/qloggingcategory.h> +#include <QtCore/qcryptographichash.h> + +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + +QT_BEGIN_NAMESPACE + +QQmlTypeData::TypeDataCallback::~TypeDataCallback() +{ +} + +QString QQmlTypeData::TypeReference::qualifiedName() const +{ + QString result; + if (!prefix.isEmpty()) { + result = prefix + QLatin1Char('.'); + } + result.append(type.qmlTypeName()); + return result; +} + +QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) + : QQmlTypeLoader::Blob(url, QmlFile, manager), + m_typesResolved(false), m_implicitImportLoaded(false) +{ + +} + +QQmlTypeData::~QQmlTypeData() +{ + m_scripts.clear(); + m_compositeSingletons.clear(); + m_resolvedTypes.clear(); +} + +const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const +{ + return m_scripts; +} + +QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const +{ + return m_compiledData.data(); +} + +void QQmlTypeData::registerCallback(TypeDataCallback *callback) +{ + Q_ASSERT(!m_callbacks.contains(callback)); + m_callbacks.append(callback); +} + +void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) +{ + Q_ASSERT(m_callbacks.contains(callback)); + m_callbacks.removeOne(callback); + Q_ASSERT(!m_callbacks.contains(callback)); +} + +bool QQmlTypeData::tryLoadFromDiskCache() +{ + if (!diskCacheEnabled()) + return false; + + QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); + if (!v4) + return false; + + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + { + QString error; + if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error; + return false; + } + } + + if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { + restoreIR(std::move(*unit)); + return true; + } + + m_compiledData = unit; + + for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) + m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + // For remote URLs, we don't delay the loading of the implicit import + // because the loading probably requires an asynchronous fetch of the + // qmldir (so we can't load it just in time). + if (!finalUrl().scheme().isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + if (!loadImplicitImport()) + return false; + + // find the implicit import + for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->importAt(i); + if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") + && import->qualifierIndex == 0 + && import->majorVersion == -1 + && import->minorVersion == -1) { + QList<QQmlError> errors; + auto pendingImport = std::make_shared<PendingImport>(this, import); + if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) { + setError(errors); + return false; + } + break; + } + } + } + } + + for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->importAt(i); + QList<QQmlError> errors; + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return false; + } + } + + return true; +} + +void QQmlTypeData::createTypeAndPropertyCaches( + const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + const QV4::ResolvedTypeReferenceMap &resolvedTypeCache) +{ + Q_ASSERT(m_compiledData); + m_compiledData->typeNameCache = typeNameCache; + m_compiledData->resolvedTypes = resolvedTypeCache; + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; + + { + QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator( + &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine, + m_compiledData.data(), &m_importCache); + QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects(); + if (error.isValid()) { + setError(error); + return; + } + } + + QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator( + &m_compiledData->propertyCaches, m_compiledData.data()); + aliasCreator.appendAliasPropertiesToMetaObjects(engine); + + pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); +} + +static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) +{ + for (const auto &typeRef: typeRefs) { + 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 = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); + bool ok = false; + hash->addData(propertyCache->checksum(&ok)); + if (!ok) + return false; + } + } + return true; +} + +void QQmlTypeData::done() +{ + auto cleanup = qScopeGuard([this]{ + m_document.reset(); + m_typeReferences.clear(); + if (isError()) + m_compiledData = nullptr; + }); + + if (isError()) + return; + + // Check all script dependencies for errors + for (int ii = 0; ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList<QQmlError> errors = script.script->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); + errors.prepend(error); + setError(errors); + return; + } + } + + // Check all type dependencies for errors + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + ++it) { + const TypeReference &type = *it; + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + const QString typeName = stringAt(it.key()); + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + return; + } + } + + // Check all composite singleton type dependencies for errors + for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { + const TypeReference &type = m_compositeSingletons.at(ii); + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + QString typeName = type.type.qmlTypeName(); + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + return; + } + } + + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QV4::ResolvedTypeReferenceMap resolvedTypeCache; + { + QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); + if (error.isValid()) { + setError(error); + qDeleteAll(resolvedTypeCache); + return; + } + } + + QQmlEngine *const engine = typeLoader()->engine(); + + const auto dependencyHasher = [engine, &resolvedTypeCache, this]() { + QCryptographicHash hash(QCryptographicHash::Md5); + return (resolvedTypeCache.addToHash(&hash, engine) + && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine)) + ? hash.result() + : QByteArray(); + }; + + // 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_backupSourceCode = SourceCodeData(); + m_compiledData = nullptr; + } + + if (!m_document.isNull()) { + // Compile component + compile(typeNameCache, &resolvedTypeCache, dependencyHasher); + } else { + createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); + } + + if (isError()) + return; + + { + QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); + { + // Sanity check property bindings + QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); + QVector<QQmlJS::DiagnosticMessage> errors = validator.validate(); + if (!errors.isEmpty()) { + setError(errors); + return; + } + } + + m_compiledData->finalizeCompositeType(enginePrivate); + } + + { + QQmlType type = QQmlMetaType::qmlType(finalUrl(), true); + if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) { + if (!type.isValid()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); + setError(error); + return; + } else if (!type.isCompositeSingleton()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName())); + setError(error); + return; + } + } else { + // If the type is CompositeSingleton but there was no pragma Singleton in the + // QML file, lets report an error. + if (type.isValid() && type.isCompositeSingleton()) { + QString typeName = type.qmlTypeName(); + setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); + return; + } + } + } + + { + // Collect imported scripts + m_compiledData->dependentScripts.reserve(m_scripts.count()); + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); + + QStringRef qualifier(&script.qualifier); + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex).toString(); + qualifier = qualifier.mid(lastDotIndex+1); + } + + m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); + m_compiledData->dependentScripts << scriptData; + } + } +} + +void QQmlTypeData::completed() +{ + // Notify callbacks + while (!m_callbacks.isEmpty()) { + TypeDataCallback *callback = m_callbacks.takeFirst(); + callback->typeDataReady(this); + } +} + +bool QQmlTypeData::loadImplicitImport() +{ + m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error) + + 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; + m_importCache.addImplicitImport(importDatabase, &implicitImportErrors); + + if (!implicitImportErrors.isEmpty()) { + setError(implicitImportErrors); + return false; + } + + return true; +} + +void QQmlTypeData::dataReceived(const SourceCodeData &data) +{ + m_backupSourceCode = data; + + if (tryLoadFromDiskCache()) + return; + + if (isError()) + return; + + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); + return; + } + + if (!loadFromSource()) + return; + + continueLoadFromIR(); +} + +void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QQmlIRLoader loader(unit, m_document.data()); + loader.load(); + m_document->jsModule.fileName = urlString(); + m_document->jsModule.finalUrl = finalUrlString(); + m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit); + continueLoadFromIR(); +} + +bool QQmlTypeData::loadFromSource() +{ + m_document.reset(new QmlIR::Document(isDebugging())); + m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); + QQmlEngine *qmlEngine = typeLoader()->engine(); + QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames()); + + QString sourceError; + const QString source = m_backupSourceCode.readAll(&sourceError); + if (!sourceError.isEmpty()) { + setError(sourceError); + return false; + } + + if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { + QList<QQmlError> errors; + errors.reserve(compiler.errors.count()); + for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { + QQmlError e; + e.setUrl(url()); + e.setLine(msg.line); + e.setColumn(msg.column); + e.setDescription(msg.message); + errors << e; + } + setError(errors); + return false; + } + return true; +} + +void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + 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); + continueLoadFromIR(); +} + +void QQmlTypeData::continueLoadFromIR() +{ + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + // For remote URLs, we don't delay the loading of the implicit import + // because the loading probably requires an asynchronous fetch of the + // qmldir (so we can't load it just in time). + if (!finalUrl().scheme().isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + if (!loadImplicitImport()) + return; + // This qmldir is for the implicit import + auto implicitImport = std::make_shared<PendingImport>(); + implicitImport->uri = QLatin1String("."); + implicitImport->majorVersion = -1; + implicitImport->minorVersion = -1; + QList<QQmlError> errors; + + if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { + setError(errors); + return; + } + } + } + + QList<QQmlError> errors; + + for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) { + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return; + } + } +} + +void QQmlTypeData::allDependenciesDone() +{ + QQmlTypeLoader::Blob::allDependenciesDone(); + + if (!m_typesResolved) { + // Check that all imports were resolved + QList<QQmlError> errors; + auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd(); + for ( ; it != end; ++it) { + if ((*it)->priority == 0) { + // This import was not resolved + for (auto keyIt = m_unresolvedImports.constBegin(), + keyEnd = m_unresolvedImports.constEnd(); + keyIt != keyEnd; ++keyIt) { + PendingImportPtr import = *keyIt; + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri)); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); + } + } + } + if (errors.size()) { + setError(errors); + return; + } + + resolveTypes(); + m_typesResolved = true; + } +} + +void QQmlTypeData::downloadProgressChanged(qreal p) +{ + for (int ii = 0; ii < m_callbacks.count(); ++ii) { + TypeDataCallback *callback = m_callbacks.at(ii); + callback->typeDataProgress(this, p); + } +} + +QString QQmlTypeData::stringAt(int index) const +{ + if (m_compiledData) + return m_compiledData->stringAt(index); + return m_document->jsGenerator.stringTable.stringForIndex(index); +} + +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::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); + + QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher); + m_compiledData = compiler.compile(); + if (!m_compiledData) { + qDeleteAll(*resolvedTypeCache); + resolvedTypeCache->clear(); + setError(compiler.compilationErrors()); + return; + } + + const bool trySaveToDisk = diskCacheEnabled() && !typeRecompilation; + if (trySaveToDisk) { + QString errorString; + if (m_compiledData->saveToDisk(url(), &errorString)) { + QString error; + if (!m_compiledData->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; + } + } +} + +void QQmlTypeData::resolveTypes() +{ + // Add any imported scripts to our resolved set + const auto resolvedScripts = m_importCache.resolvedScripts(); + for (const QQmlImports::ScriptReference &script : resolvedScripts) { + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); + addDependency(blob.data()); + + ScriptReference ref; + //ref.location = ... + if (!script.qualifier.isEmpty()) + { + ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; + // Add a reference to the enclosing namespace + m_namespaces.insert(script.qualifier); + } else { + ref.qualifier = script.nameSpace; + } + + ref.script = blob; + m_scripts << ref; + } + + // Lets handle resolved composite singleton types + const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons(); + for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) { + TypeReference ref; + QString typeName; + if (!csRef.prefix.isEmpty()) { + typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; + // Add a reference to the enclosing namespace + m_namespaces.insert(csRef.prefix); + } else { + typeName = csRef.typeName; + } + + int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; + int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; + + if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true, + QQmlType::CompositeSingletonType)) + return; + + if (ref.type.isCompositeSingleton()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); + if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies || m_waitingOnMe.contains(ref.typeData.data())) { + // TODO: give an error message? If so, we should record and show the path of the cycle. + continue; + } + addDependency(ref.typeData.data()); + ref.prefix = csRef.prefix; + + m_compositeSingletons << ref; + } + } + + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); + unresolvedRef != end; ++unresolvedRef) { + + TypeReference ref; // resolved reference + + const bool reportErrors = unresolvedRef->errorWhenNotFound; + + int majorVersion = -1; + int minorVersion = -1; + + const QString name = stringAt(unresolvedRef.key()); + + if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, + unresolvedRef->location.column, reportErrors, + QQmlType::AnyRegistrationType) && reportErrors) + return; + + if (ref.type.isComposite()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); + addDependency(ref.typeData.data()); + } + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; + + ref.location.line = unresolvedRef->location.line; + ref.location.column = unresolvedRef->location.column; + + ref.needsCreation = unresolvedRef->needsCreation; + + m_resolvedTypes.insert(unresolvedRef.key(), ref); + } + + // ### this allows enums to work without explicit import or instantiation of the type + if (!m_implicitImportLoaded) + loadImplicitImport(); +} + +QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache + ) const +{ + typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); + + for (const QString &ns: m_namespaces) + (*typeNameCache)->add(ns); + + // Add any Composite Singletons that were used to the import cache + for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) + (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); + + m_importCache.populateCache(typeNameCache->data()); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { + QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference); + QQmlType qmlType = resolvedType->type; + if (resolvedType->typeData) { + if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) { + return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); + } + ref->compilationUnit = resolvedType->typeData->compilationUnit(); + } else if (qmlType.isValid()) { + ref->type = qmlType; + Q_ASSERT(ref->type.isValid()); + + if (resolvedType->needsCreation && !ref->type.isCreatable()) { + QString reason = ref->type.noCreationReason(); + if (reason.isEmpty()) + reason = tr("Element is not creatable."); + return qQmlCompileError(resolvedType->location, reason); + } + + if (ref->type.containsRevisionedAttributes()) { + ref->typePropertyCache = engine->cache(ref->type, + resolvedType->minorVersion); + } + } + ref->majorVersion = resolvedType->majorVersion; + ref->minorVersion = resolvedType->minorVersion; + ref->doDynamicTypeCheck(); + resolvedTypeCache->insert(resolvedType.key(), ref.take()); + } + QQmlJS::DiagnosticMessage noError; + return noError; +} + +bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, + TypeReference &ref, int lineNumber, int columnNumber, + bool reportErrors, QQmlType::RegistrationType registrationType) +{ + QQmlImportNamespace *typeNamespace = nullptr; + QList<QQmlError> errors; + + bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, + &typeNamespace, &errors, registrationType); + 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, &majorVersion, &minorVersion, + &typeNamespace, &errors, registrationType); + } else { + return false; //loadImplicitImport() hit an error, and called setError already + } + } + + if ((!typeFound || typeNamespace) && reportErrors) { + // Known to not be a type: + // - known to be a namespace (Namespace {}) + // - type with unknown namespace (UnknownNamespace.SomeType {}) + QQmlError error; + if (typeNamespace) { + error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName)); + } else { + if (errors.size()) { + error = errors.takeFirst(); + } else { + // this should not be possible! + // Description should come from error provided by addImport() function. + error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database")); + } + error.setUrl(m_importCache.baseUrl()); + error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description())); + } + + if (lineNumber != -1) + error.setLine(lineNumber); + if (columnNumber != -1) + error.setColumn(columnNumber); + + errors.prepend(error); + setError(errors); + return false; + } + + return true; +} + +void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) +{ + ScriptReference ref; + ref.script = blob; + ref.location = location; + ref.qualifier = qualifier; + + m_scripts << ref; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h new file mode 100644 index 0000000000..e1d0c900ea --- /dev/null +++ b/src/qml/qml/qqmltypedata_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPEDATA_P_H +#define QQMLTYPEDATA_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 <private/qqmltypeloader_p.h> +#include <private/qv4executablecompilationunit_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob +{ + Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) +public: + struct TypeReference + { + TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {} + + QV4::CompiledData::Location location; + QQmlType type; + int majorVersion; + int minorVersion; + QQmlRefPointer<QQmlTypeData> typeData; + QString prefix; // used by CompositeSingleton types + QString qualifiedName() const; + bool needsCreation; + }; + + struct ScriptReference + { + QV4::CompiledData::Location location; + QString qualifier; + QQmlRefPointer<QQmlScriptBlob> script; + }; + +private: + friend class QQmlTypeLoader; + + QQmlTypeData(const QUrl &, QQmlTypeLoader *); + +public: + ~QQmlTypeData() override; + + const QList<ScriptReference> &resolvedScripts() const; + + QV4::ExecutableCompilationUnit *compilationUnit() const; + + // Used by QQmlComponent to get notifications + struct TypeDataCallback { + virtual ~TypeDataCallback(); + virtual void typeDataProgress(QQmlTypeData *, qreal) {} + virtual void typeDataReady(QQmlTypeData *) {} + }; + void registerCallback(TypeDataCallback *); + void unregisterCallback(TypeDataCallback *); + +protected: + void done() override; + void completed() override; + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; + void allDependenciesDone() override; + void downloadProgressChanged(qreal) override; + + QString stringAt(int index) const override; + +private: + bool tryLoadFromDiskCache(); + bool loadFromSource(); + void restoreIR(QV4::CompiledData::CompilationUnit &&unit); + void continueLoadFromIR(); + void resolveTypes(); + QQmlJS::DiagnosticMessage buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache + ) const; + void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher); + void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + const QV4::ResolvedTypeReferenceMap &resolvedTypeCache); + bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, + TypeReference &ref, int lineNumber = -1, int columnNumber = -1, + bool reportErrors = true, + QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType); + + void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; + + + SourceCodeData m_backupSourceCode; // used when cache verification fails. + QScopedPointer<QmlIR::Document> m_document; + QV4::CompiledData::TypeReferenceMap m_typeReferences; + + QList<ScriptReference> m_scripts; + + QSet<QString> m_namespaces; + QList<TypeReference> m_compositeSingletons; + + // map from name index to resolved type + // While this could be a hash, a map is chosen here to provide a stable + // order, which is used to calculating a check-sum on dependent meta-objects. + QMap<int, TypeReference> m_resolvedTypes; + bool m_typesResolved:1; + + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData; + + QList<TypeDataCallback *> m_callbacks; + + bool m_implicitImportLoaded; + bool loadImplicitImport(); +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEDATA_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5d3b1cedc5..c6c3ee5523 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -37,77 +37,37 @@ ** ****************************************************************************/ -#include "qqmltypeloader_p.h" -#include "qqmlabstracturlinterceptor.h" -#include "qqmlexpression_p.h" - -#include <private/qqmlengine_p.h> -#include <private/qqmlglobal_p.h> -#include <private/qqmlthread_p.h> -#include <private/qv4codegen_p.h> -#include <private/qqmlcomponent_p.h> +#include <private/qqmltypeloader_p.h> + +#include <private/qqmldirdata_p.h> #include <private/qqmlprofiler_p.h> -#include <private/qqmlmemoryprofiler_p.h> -#include <private/qqmltypecompiler_p.h> -#include <private/qqmlpropertyvalidator_p.h> -#include <private/qqmlpropertycachecreator_p.h> -#include <private/qv4module_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmltypedata_p.h> +#include <private/qqmltypeloaderqmldircontent_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#include <QtQml/qqmlabstracturlinterceptor.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlextensioninterface.h> +#include <QtQml/qqmlfile.h> #include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> #include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmutex.h> #include <QtCore/qthread.h> -#include <QtQml/qqmlfile.h> -#include <QtCore/qdiriterator.h> -#include <QtQml/qqmlcomponent.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qloggingcategory.h> -#include <QtQml/qqmlextensioninterface.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/qscopeguard.h> #include <functional> -#if defined (Q_OS_UNIX) -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#endif - -#if defined (QT_LINUXBASE) -// LSB doesn't declare NAME_MAX. Use SYMLINK_MAX instead, which seems to -// always be identical to NAME_MAX -#ifndef NAME_MAX -# define NAME_MAX _POSIX_SYMLINK_MAX -#endif - -#endif - // #define DATABLOB_DEBUG - #ifdef DATABLOB_DEBUG - -#define ASSERT_MAINTHREAD() do { if (m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in main thread"); } while (false) #define ASSERT_LOADTHREAD() do { if (!m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in load thread"); } while (false) -#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) - #else - -#define ASSERT_MAINTHREAD() #define ASSERT_LOADTHREAD() -#define ASSERT_CALLBACK() - #endif -DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); -Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) -Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") - QT_BEGIN_NAMESPACE namespace { @@ -121,830 +81,6 @@ namespace { }; } -#if QT_CONFIG(qml_network) -// This is a lame object that we need to ensure that slots connected to -// QNetworkReply get called in the correct thread (the loader thread). -// As QQmlTypeLoader lives in the main thread, and we can't use -// Qt::DirectConnection connections from a QNetworkReply (because then -// sender() wont work), we need to insert this object in the middle. -class QQmlTypeLoaderNetworkReplyProxy : public QObject -{ - Q_OBJECT -public: - QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l); - -public slots: - void finished(); - void downloadProgress(qint64, qint64); - void manualFinished(QNetworkReply*); - -private: - QQmlTypeLoader *l; -}; -#endif // qml_network - -class QQmlTypeLoaderThread : public QQmlThread -{ - typedef QQmlTypeLoaderThread This; - -public: - QQmlTypeLoaderThread(QQmlTypeLoader *loader); -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() const; - QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; -#endif // qml_network - void load(QQmlDataBlob *b); - void loadAsync(QQmlDataBlob *b); - void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); - void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &); - void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void callCompleted(QQmlDataBlob *b); - void callDownloadProgressChanged(QQmlDataBlob *b, qreal p); - void initializeEngine(QQmlExtensionInterface *, const char *); - -protected: - void shutdownThread() override; - -private: - void loadThread(QQmlDataBlob *b); - void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &); - void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void callCompletedMain(QQmlDataBlob *b); - void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p); - void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); - - QQmlTypeLoader *m_loader; -#if QT_CONFIG(qml_network) - mutable QNetworkAccessManager *m_networkAccessManager; - mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; -#endif // qml_network -}; - -#if QT_CONFIG(qml_network) -QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) -: l(l) -{ -} - -void QQmlTypeLoaderNetworkReplyProxy::finished() -{ - Q_ASSERT(sender()); - Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); - QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); - l->networkReplyFinished(reply); -} - -void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - Q_ASSERT(sender()); - Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); - QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); - l->networkReplyProgress(reply, bytesReceived, bytesTotal); -} - -// This function is for when you want to shortcut the signals and call directly -void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) -{ - qint64 replySize = reply->size(); - l->networkReplyProgress(reply, replySize, replySize); - l->networkReplyFinished(reply); -} -#endif // qml_network - -/*! -\class QQmlDataBlob -\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader. -\internal - -QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob -and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it. -The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available. -*/ - -/*! -\enum QQmlDataBlob::Status - -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 continueing. 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 -*/ - -/*! -\enum QQmlDataBlob::Type - -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 -*/ - -/*! -Create a new QQmlDataBlob for \a url and of the provided \a type. -*/ -QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager) -: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0), - m_inCallback(false), m_isDone(false) -{ - //Set here because we need to get the engine from the manager - if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor()) - m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url, - (QQmlAbstractUrlInterceptor::DataType)m_type); -} - -/*! \internal */ -QQmlDataBlob::~QQmlDataBlob() -{ - Q_ASSERT(m_waitingOnMe.isEmpty()); - - cancelAllWaitingFor(); -} - -/*! - Must be called before loading can occur. -*/ -void QQmlDataBlob::startLoading() -{ - Q_ASSERT(status() == QQmlDataBlob::Null); - m_data.setStatus(QQmlDataBlob::Loading); -} - -/*! -Returns the type provided to the constructor. -*/ -QQmlDataBlob::Type QQmlDataBlob::type() const -{ - return m_type; -} - -/*! -Returns the blob's status. -*/ -QQmlDataBlob::Status QQmlDataBlob::status() const -{ - return m_data.status(); -} - -/*! -Returns true if the status is Null. -*/ -bool QQmlDataBlob::isNull() const -{ - return status() == Null; -} - -/*! -Returns true if the status is Loading. -*/ -bool QQmlDataBlob::isLoading() const -{ - return status() == Loading; -} - -/*! -Returns true if the status is WaitingForDependencies. -*/ -bool QQmlDataBlob::isWaiting() const -{ - return status() == WaitingForDependencies || - status() == ResolvingDependencies; -} - -/*! -Returns true if the status is Complete. -*/ -bool QQmlDataBlob::isComplete() const -{ - return status() == Complete; -} - -/*! -Returns true if the status is Error. -*/ -bool QQmlDataBlob::isError() const -{ - return status() == Error; -} - -/*! -Returns true if the status is Complete or Error. -*/ -bool QQmlDataBlob::isCompleteOrError() const -{ - Status s = status(); - return s == Error || s == Complete; -} - -/*! -Returns the data download progress from 0 to 1. -*/ -qreal QQmlDataBlob::progress() const -{ - quint8 p = m_data.progress(); - if (p == 0xFF) return 1.; - else return qreal(p) / qreal(0xFF); -} - -/*! -Returns the physical url of the data. Initially this is the same as -finalUrl(), but if a URL interceptor is set, it will work on this URL -and leave finalUrl() alone. - -\sa finalUrl() -*/ -QUrl QQmlDataBlob::url() const -{ - return m_url; -} - -QString QQmlDataBlob::urlString() const -{ - if (m_urlString.isEmpty()) - m_urlString = m_url.toString(); - - return m_urlString; -} - -/*! -Returns the logical URL to be used for resolving further URLs referred to in -the code. - -This is the blob url passed to the constructor. If a URL interceptor rewrites -the URL, this one stays the same. If a network redirect happens while fetching -the data, this url is updated to reflect the new location. Therefore, if both -an interception and a redirection happen, the final url will indirectly -incorporate the result of the interception, potentially breaking further -lookups. - -\sa url() -*/ -QUrl QQmlDataBlob::finalUrl() const -{ - return m_finalUrl; -} - -/*! -Returns the finalUrl() as a string. -*/ -QString QQmlDataBlob::finalUrlString() const -{ - if (m_finalUrlString.isEmpty()) - m_finalUrlString = m_finalUrl.toString(); - - return m_finalUrlString; -} - -/*! -Return the errors on this blob. - -May only be called from the load thread, or after the blob isCompleteOrError(). -*/ -QList<QQmlError> QQmlDataBlob::errors() const -{ - Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); - return m_errors; -} - -/*! -Mark this blob as having \a errors. - -All outstanding dependencies will be cancelled. Requests to add new dependencies -will be ignored. Entry into the Error state is irreversable. - -The setError() method may only be called from within a QQmlDataBlob callback. -*/ -void QQmlDataBlob::setError(const QQmlError &errors) -{ - ASSERT_CALLBACK(); - - QList<QQmlError> l; - l << errors; - setError(l); -} - -/*! -\overload -*/ -void QQmlDataBlob::setError(const QList<QQmlError> &errors) -{ - ASSERT_CALLBACK(); - - Q_ASSERT(status() != Error); - Q_ASSERT(m_errors.isEmpty()); - - m_errors = errors; // Must be set before the m_data fence - m_data.setStatus(Error); - - if (dumpErrors()) { - qWarning().nospace() << "Errors for " << urlString(); - for (int ii = 0; ii < errors.count(); ++ii) - qWarning().nospace() << " " << qPrintable(errors.at(ii).toString()); - } - cancelAllWaitingFor(); - - if (!m_inCallback) - tryDone(); -} - -void QQmlDataBlob::setError(const QQmlCompileError &error) -{ - QQmlError e; - e.setColumn(error.location.column); - e.setLine(error.location.line); - e.setDescription(error.description); - e.setUrl(url()); - setError(e); -} - -void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors) -{ - QList<QQmlError> finalErrors; - finalErrors.reserve(errors.count()); - for (const QQmlCompileError &error: errors) { - QQmlError e; - e.setColumn(error.location.column); - e.setLine(error.location.line); - e.setDescription(error.description); - e.setUrl(url()); - finalErrors << e; - } - setError(finalErrors); -} - -void QQmlDataBlob::setError(const QString &description) -{ - QQmlError e; - e.setDescription(description); - e.setUrl(url()); - setError(e); -} - -/*! -Wait for \a blob to become complete or to error. If \a blob is already -complete or in error, or this blob is already complete, this has no effect. - -The setError() method may only be called from within a QQmlDataBlob callback. -*/ -void QQmlDataBlob::addDependency(QQmlDataBlob *blob) -{ - ASSERT_CALLBACK(); - - Q_ASSERT(status() != Null); - - if (!blob || - blob->status() == Error || blob->status() == Complete || - status() == Error || status() == Complete || m_isDone) - return; - - for (auto existingDep: qAsConst(m_waitingFor)) - if (existingDep.data() == blob) - return; - - m_data.setStatus(WaitingForDependencies); - - m_waitingFor.append(blob); - blob->m_waitingOnMe.append(this); -} - -/*! -\fn void QQmlDataBlob::dataReceived(const Data &data) - -Invoked when data for the blob is received. Implementors should use this callback -to determine a blob's dependencies. Within this callback you may call setError() -or addDependency(). -*/ - -/*! -Invoked once data has either been received or a network error occurred, and all -dependencies are complete. - -You can set an error in this method, but you cannot add new dependencies. Implementors -should use this callback to finalize processing of data. - -The default implementation does nothing. - -XXX Rename processData() or some such to avoid confusion between done() (processing thread) -and completed() (main thread) -*/ -void QQmlDataBlob::done() -{ -} - -#if QT_CONFIG(qml_network) -/*! -Invoked if there is a network error while fetching this blob. - -The default implementation sets an appropriate QQmlError. -*/ -void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) -{ - Q_UNUSED(networkError); - - QQmlError error; - error.setUrl(m_url); - - const char *errorString = nullptr; - switch (networkError) { - default: - errorString = "Network error"; - break; - case QNetworkReply::ConnectionRefusedError: - errorString = "Connection refused"; - break; - case QNetworkReply::RemoteHostClosedError: - errorString = "Remote host closed the connection"; - break; - case QNetworkReply::HostNotFoundError: - errorString = "Host not found"; - break; - case QNetworkReply::TimeoutError: - errorString = "Timeout"; - break; - case QNetworkReply::ProxyConnectionRefusedError: - case QNetworkReply::ProxyConnectionClosedError: - case QNetworkReply::ProxyNotFoundError: - case QNetworkReply::ProxyTimeoutError: - case QNetworkReply::ProxyAuthenticationRequiredError: - case QNetworkReply::UnknownProxyError: - errorString = "Proxy error"; - break; - case QNetworkReply::ContentAccessDenied: - errorString = "Access denied"; - break; - case QNetworkReply::ContentNotFoundError: - errorString = "File not found"; - break; - case QNetworkReply::AuthenticationRequiredError: - errorString = "Authentication required"; - break; - }; - - error.setDescription(QLatin1String(errorString)); - - setError(error); -} -#endif // qml_network - -/*! -Called if \a blob, which was previously waited for, has an error. - -The default implementation does nothing. -*/ -void QQmlDataBlob::dependencyError(QQmlDataBlob *blob) -{ - Q_UNUSED(blob); -} - -/*! -Called if \a blob, which was previously waited for, has completed. - -The default implementation does nothing. -*/ -void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob) -{ - Q_UNUSED(blob); -} - -/*! -Called when all blobs waited for have completed. This occurs regardless of -whether they are in error, or complete state. - -The default implementation does nothing. -*/ -void QQmlDataBlob::allDependenciesDone() -{ - m_data.setStatus(QQmlDataBlob::ResolvingDependencies); -} - -/*! -Called when the download progress of this blob changes. \a progress goes -from 0 to 1. - -This callback is only invoked if an asynchronous load for this blob is -made. An asynchronous load is one in which the Asynchronous mode is -specified explicitly, or one that is implicitly delayed due to a network -operation. - -The default implementation does nothing. -*/ -void QQmlDataBlob::downloadProgressChanged(qreal progress) -{ - Q_UNUSED(progress); -} - -/*! -Invoked on the main thread sometime after done() was called on the load thread. - -You cannot modify the blobs state at all in this callback and cannot depend on the -order or timeliness of these callbacks. Implementors should use this callback to notify -dependencies on the main thread that the blob is done and not a lot else. - -This callback is only invoked if an asynchronous load for this blob is -made. An asynchronous load is one in which the Asynchronous mode is -specified explicitly, or one that is implicitly delayed due to a network -operation. - -The default implementation does nothing. -*/ -void QQmlDataBlob::completed() -{ -} - - -void QQmlDataBlob::tryDone() -{ - if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { - m_isDone = true; - addref(); - -#ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob::done() %s", qPrintable(urlString())); -#endif - done(); - - if (status() != Error) - m_data.setStatus(Complete); - - notifyAllWaitingOnMe(); - - // Locking is not required here, as anyone expecting callbacks must - // already be protected against the blob being completed (as set above); -#ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob: Dispatching completed"); -#endif - m_typeLoader->m_thread->callCompleted(this); - - release(); - } -} - -void QQmlDataBlob::cancelAllWaitingFor() -{ - while (m_waitingFor.count()) { - QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast(); - - Q_ASSERT(blob->m_waitingOnMe.contains(this)); - - blob->m_waitingOnMe.removeOne(this); - } -} - -void QQmlDataBlob::notifyAllWaitingOnMe() -{ - while (m_waitingOnMe.count()) { - QQmlDataBlob *blob = m_waitingOnMe.takeLast(); - - Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(), - [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; })); - - blob->notifyComplete(this); - } -} - -void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) -{ - Q_ASSERT(blob->status() == Error || blob->status() == Complete); - QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); - - m_inCallback = true; - - QQmlRefPointer<QQmlDataBlob> blobRef; - for (int i = 0; i < m_waitingFor.count(); ++i) { - if (m_waitingFor.at(i).data() == blob) { - blobRef = m_waitingFor.takeAt(i); - break; - } - } - Q_ASSERT(blobRef); - - if (blob->status() == Error) { - dependencyError(blob); - } else if (blob->status() == Complete) { - dependencyComplete(blob); - } - - if (!isError() && m_waitingFor.isEmpty()) - allDependenciesDone(); - - m_inCallback = false; - - tryDone(); -} - -#define TD_STATUS_MASK 0x0000FFFF -#define TD_STATUS_SHIFT 0 -#define TD_PROGRESS_MASK 0x00FF0000 -#define TD_PROGRESS_SHIFT 16 -#define TD_ASYNC_MASK 0x80000000 - -QQmlDataBlob::ThreadData::ThreadData() -: _p(0) -{ -} - -QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const -{ - return QQmlDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status) -{ - while (true) { - int d = _p.load(); - int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -bool QQmlDataBlob::ThreadData::isAsync() const -{ - return _p.load() & TD_ASYNC_MASK; -} - -void QQmlDataBlob::ThreadData::setIsAsync(bool v) -{ - while (true) { - int d = _p.load(); - int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -quint8 QQmlDataBlob::ThreadData::progress() const -{ - return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setProgress(quint8 v) -{ - while (true) { - int d = _p.load(); - int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) -: m_loader(loader) -#if QT_CONFIG(qml_network) -, m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr) -#endif // qml_network -{ - // Do that after initializing all the members. - startup(); -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const -{ - Q_ASSERT(isThisThread()); - if (!m_networkAccessManager) { - m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr); - m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader); - } - - return m_networkAccessManager; -} - -QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const -{ - Q_ASSERT(isThisThread()); - Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first - return m_networkReplyProxy; -} -#endif // qml_network - -void QQmlTypeLoaderThread::load(QQmlDataBlob *b) -{ - b->addref(); - callMethodInThread(&This::loadThread, b); -} - -void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b) -{ - b->addref(); - postMethodToThread(&This::loadThread, b); -} - -void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d) -{ - b->addref(); - callMethodInThread(&This::loadWithStaticDataThread, b, d); -} - -void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d) -{ - b->addref(); - postMethodToThread(&This::loadWithStaticDataThread, b, d); -} - -void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - b->addref(); - callMethodInThread(&This::loadWithCachedUnitThread, b, unit); -} - -void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - b->addref(); - postMethodToThread(&This::loadWithCachedUnitThread, b, unit); -} - -void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b) -{ - b->addref(); -#if !QT_CONFIG(thread) - if (!isThisThread()) - postMethodToThread(&This::callCompletedMain, b); -#else - postMethodToMain(&This::callCompletedMain, b); -#endif -} - -void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) -{ - b->addref(); -#if !QT_CONFIG(thread) - if (!isThisThread()) - postMethodToThread(&This::callDownloadProgressChangedMain, b, p); -#else - postMethodToMain(&This::callDownloadProgressChangedMain, b, p); -#endif -} - -void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, - const char *uri) -{ - callMethodInMain(&This::initializeEngineMain, iface, uri); -} - -void QQmlTypeLoaderThread::shutdownThread() -{ -#if QT_CONFIG(qml_network) - delete m_networkAccessManager; - m_networkAccessManager = nullptr; - delete m_networkReplyProxy; - m_networkReplyProxy = nullptr; -#endif // qml_network -} - -void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) -{ - m_loader->loadThread(b); - b->release(); -} - -void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d) -{ - m_loader->loadWithStaticDataThread(b, d); - b->release(); -} - -void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - m_loader->loadWithCachedUnitThread(b, unit); - b->release(); -} - -void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b) -{ - QML_MEMORY_SCOPE_URL(b->url()); -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString())); -#endif - b->completed(); - b->release(); -} - -void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) -{ -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback", - qPrintable(b->urlString()), p); -#endif - b->downloadProgressChanged(p); - b->release(); -} - -void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, - const char *uri) -{ - Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); - iface->initializeEngine(m_loader->engine(), uri); -} - /*! \class QQmlTypeLoader \brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network. @@ -1146,8 +282,6 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) return; } - QML_MEMORY_SCOPE_URL(blob->m_url); - if (QQmlFile::isSynchronous(blob->m_url)) { const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url); if (!QQml_isFileCaseCorrect(fileName)) { @@ -1277,7 +411,6 @@ void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface, void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); d.hasInlineSourceCode = true; @@ -1286,7 +419,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.fileInfo = QFileInfo(fileName); setData(blob, d); @@ -1294,7 +426,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1314,7 +445,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1338,6 +468,16 @@ void QQmlTypeLoader::shutdownThread() m_thread->shutdown(); } +QQmlTypeLoader::Blob::PendingImport::PendingImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) +{ + type = static_cast<QV4::CompiledData::Import::ImportType>(quint32(import->type)); + uri = blob->stringAt(import->uriIndex); + qualifier = blob->stringAt(import->qualifierIndex); + majorVersion = import->majorVersion; + minorVersion = import->minorVersion; + location = import->location; +} + QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader) : QQmlDataBlob(url, type, loader), m_importCache(loader) { @@ -1347,11 +487,11 @@ QQmlTypeLoader::Blob::~Blob() { } -bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors) { QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url); - data->setImport(this, import); + data->setImport(this, std::move(import)); data->setPriority(this, priority); if (data->status() == Error) { @@ -1367,26 +507,25 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData: return true; } -bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors) { QString qmldirIdentifier = data->urlString(); QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1); typeLoader()->setQmldirContent(qmldirIdentifier, data->content()); - if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), stringAt(import->uriIndex), stringAt(import->qualifierIndex), qmldirIdentifier, qmldirUrl, errors)) + if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors)) return false; - QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import); - if (it != m_unresolvedImports.end()) { - *it = data->priority(this); - } + if (!loadImportDependencies(import, qmldirIdentifier, errors)) + return false; + + import->priority = data->priority(this); // Release this reference at destruction m_qmldirs << data; - const QString &importQualifier = stringAt(import->qualifierIndex); - if (!importQualifier.isEmpty()) { + if (!import->qualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); @@ -1396,7 +535,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, script.nameSpace, importQualifier); + scriptImported(blob, import->location, script.nameSpace, import->qualifier); } } @@ -1405,36 +544,42 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors) { + return addImport(std::make_shared<PendingImport>(this, import), errors); +} + +bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr import, QList<QQmlError> *errors) +{ Q_ASSERT(errors); QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); - const QString &importUri = stringAt(import->uriIndex); - const QString &importQualifier = stringAt(import->qualifierIndex); if (import->type == QV4::CompiledData::Import::ImportScript) { - QUrl scriptUrl = finalUrl().resolved(QUrl(importUri)); + QUrl scriptUrl = finalUrl().resolved(QUrl(import->uri)); QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, importQualifier, QString()); + scriptImported(blob, import->location, import->qualifier, QString()); } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { QString qmldirFilePath; QString qmldirUrl; - if (QQmlMetaType::isLockedModule(importUri, import->majorVersion)) { + if (QQmlMetaType::isLockedModule(import->uri, import->majorVersion)) { //Locked modules are checked first, to save on filesystem checks - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), false, errors)) return false; - } else if (m_importCache.locateQmldir(importDatabase, importUri, import->majorVersion, import->minorVersion, + } else if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion, &qmldirFilePath, &qmldirUrl)) { // This is a local library import - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, qmldirFilePath, qmldirUrl, false, errors)) return false; - if (!importQualifier.isEmpty()) { + if (!loadImportDependencies(import, qmldirFilePath, errors)) + return false; + + if (!import->qualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); @@ -1444,18 +589,18 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, script.nameSpace, importQualifier); + scriptImported(blob, import->location, script.nameSpace, import->qualifier); } } } else { // Is this a module? - if (QQmlMetaType::isAnyModule(importUri)) { - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (QQmlMetaType::isAnyModule(import->uri)) { + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), false, errors)) return false; } else { // We haven't yet resolved this import - m_unresolvedImports.insert(import, 0); + m_unresolvedImports << import; QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor(); @@ -1466,13 +611,13 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL : QQmlImportDatabase::Remote); if (!remotePathList.isEmpty()) { // Add this library and request the possible locations for it - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), true, errors)) return false; // Probe for all possible locations int priority = 0; - const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(importUri, remotePathList, import->majorVersion, import->minorVersion); + const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion); for (const QString &qmldirPath : qmlDirPaths) { if (interceptor) { QUrl url = interceptor->intercept( @@ -1495,7 +640,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL bool incomplete = false; - QUrl importUrl(importUri); + QUrl importUrl(import->uri); QString path = importUrl.path(); path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir")); importUrl.setPath(path); @@ -1505,7 +650,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL incomplete = true; } - if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addFileImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, incomplete, errors)) return false; @@ -1523,7 +668,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) if (blob->type() == QQmlDataBlob::QmldirFile) { QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob); - const QV4::CompiledData::Import *import = data->import(this); + PendingImportPtr import = data->import(this); QList<QQmlError> errors; if (!qmldirDataAvailable(data, &errors)) { @@ -1538,16 +683,34 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) } } +bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors) +{ + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri); + for (const QString &implicitImports: qmldir.imports()) { + auto dependencyImport = std::make_shared<PendingImport>(); + dependencyImport->uri = implicitImports; + dependencyImport->qualifier = currentImport->qualifier; + dependencyImport->majorVersion = currentImport->majorVersion; + dependencyImport->minorVersion = currentImport->minorVersion; + if (!addImport(dependencyImport, errors)) + return false; + } + return true; +} + bool QQmlTypeLoader::Blob::isDebugging() const { return typeLoader()->engine()->handle()->debugger() != nullptr; } -bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::diskCacheEnabled() const { - bool resolve = true; + return (!disableDiskCache() || forceDiskCache()) && !isDebugging(); +} - const QV4::CompiledData::Import *import = data->import(this); +bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +{ + PendingImportPtr import = data->import(this); data->setImport(this, nullptr); int priority = data->priority(this); @@ -1555,10 +718,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa if (import) { // Do we need to resolve this import? - QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import); - if (it != m_unresolvedImports.end()) { - resolve = (*it == 0) || (*it > priority); - } + const bool resolve = (import->priority == 0) || (import->priority > priority); if (resolve) { // This is the (current) best resolution for this import @@ -1566,8 +726,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa return false; } - if (it != m_unresolvedImports.end()) - *it = priority; + import->priority = priority; return true; } } @@ -1575,63 +734,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa return true; } - -QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() -{ -} - -bool QQmlTypeLoaderQmldirContent::hasError() const -{ - return m_parser.hasError(); -} - -QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const -{ - return m_parser.errors(uri); -} - -QString QQmlTypeLoaderQmldirContent::typeNamespace() const -{ - return m_parser.typeNamespace(); -} - -void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) -{ - m_hasContent = true; - m_location = location; - m_parser.parse(content); -} - -void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) -{ - m_parser.setError(error); -} - -QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const -{ - return m_parser.components(); -} - -QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const -{ - return m_parser.scripts(); -} - -QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const -{ - return m_parser.plugins(); -} - -QString QQmlTypeLoaderQmldirContent::pluginLocation() const -{ - return m_location; -} - -bool QQmlTypeLoaderQmldirContent::designerSupported() const -{ - return m_parser.designerSupported(); -} - /*! Constructs a new type loader that uses the given \a engine. */ @@ -1785,17 +887,6 @@ QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url) return qmldirData; } -// #### Qt 6: Remove this function, it exists only for binary compatibility. -/*! - * \internal - */ -bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName) -{ - Q_UNUSED(name) - Q_UNUSED(fileName) - return false; -} - /*! Returns the absolute filename of path via a directory cache. Returns a empty string if the path does not exist. @@ -1868,8 +959,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) { - if (path.isEmpty()) + const QChar nullChar(QChar::Null); + if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar)) return false; + Q_ASSERT(path.endsWith(QLatin1Char('/'))); if (path.at(0) == QLatin1Char(':')) { // qrc resource @@ -2106,1215 +1199,4 @@ bool QQmlTypeLoader::isScriptLoaded(const QUrl &url) const return m_scriptCache.contains(url); } -QQmlTypeData::TypeDataCallback::~TypeDataCallback() -{ -} - -QString QQmlTypeData::TypeReference::qualifiedName() const -{ - QString result; - if (!prefix.isEmpty()) { - result = prefix + QLatin1Char('.'); - } - result.append(type.qmlTypeName()); - return result; -} - -QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) -: QQmlTypeLoader::Blob(url, QmlFile, manager), - m_typesResolved(false), m_implicitImportLoaded(false) -{ - -} - -QQmlTypeData::~QQmlTypeData() -{ - m_scripts.clear(); - m_compositeSingletons.clear(); - m_resolvedTypes.clear(); -} - -const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const -{ - return m_scripts; -} - -QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const -{ - return m_compiledData.data(); -} - -void QQmlTypeData::registerCallback(TypeDataCallback *callback) -{ - Q_ASSERT(!m_callbacks.contains(callback)); - m_callbacks.append(callback); -} - -void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) -{ - Q_ASSERT(m_callbacks.contains(callback)); - m_callbacks.removeOne(callback); - Q_ASSERT(!m_callbacks.contains(callback)); -} - -bool QQmlTypeData::tryLoadFromDiskCache() -{ - if (disableDiskCache() && !forceDiskCache()) - return false; - - if (isDebugging()) - return false; - - QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); - if (!v4) - return false; - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - { - QString error; - if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { - qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error; - return false; - } - } - - if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { - restoreIR(unit); - return true; - } - - m_compiledData = unit; - - for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) - m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); - - m_importCache.setBaseUrl(finalUrl(), finalUrlString()); - - // For remote URLs, we don't delay the loading of the implicit import - // because the loading probably requires an asynchronous fetch of the - // qmldir (so we can't load it just in time). - if (!finalUrl().scheme().isEmpty()) { - QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); - if (!QQmlImports::isLocal(qmldirUrl)) { - if (!loadImplicitImport()) - return false; - - // find the implicit import - for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) { - const QV4::CompiledData::Import *import = m_compiledData->importAt(i); - if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") - && import->qualifierIndex == 0 - && import->majorVersion == -1 - && import->minorVersion == -1) { - QList<QQmlError> errors; - if (!fetchQmldir(qmldirUrl, import, 1, &errors)) { - setError(errors); - return false; - } - break; - } - } - } - } - - for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) { - const QV4::CompiledData::Import *import = m_compiledData->importAt(i); - QList<QQmlError> errors; - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return false; - } - } - - return true; -} - -void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) -{ - Q_ASSERT(m_compiledData); - m_compiledData->typeNameCache = typeNameCache; - m_compiledData->resolvedTypes = resolvedTypeCache; - - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); - - QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; - - { - QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, - &pendingGroupPropertyBindings, - engine, m_compiledData.data(), &m_importCache); - QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); - if (error.isSet()) { - setError(error); - return; - } - } - - QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData.data()); - aliasCreator.appendAliasPropertiesToMetaObjects(); - - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); -} - -static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) -{ - for (const auto &typeRef: typeRefs) { - 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 = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); - bool ok = false; - hash->addData(propertyCache->checksum(&ok)); - if (!ok) - return false; - } - } - return true; -} - -void QQmlTypeData::done() -{ - auto cleanup = qScopeGuard([this]{ - m_document.reset(); - m_typeReferences.clear(); - if (isError()) - m_compiledData = nullptr; - }); - - if (isError()) - return; - - // Check all script dependencies for errors - for (int ii = 0; ii < m_scripts.count(); ++ii) { - const ScriptReference &script = m_scripts.at(ii); - Q_ASSERT(script.script->isCompleteOrError()); - if (script.script->isError()) { - QList<QQmlError> errors = script.script->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(script.location.line); - error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); - errors.prepend(error); - setError(errors); - return; - } - } - - // Check all type dependencies for errors - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; - ++it) { - const TypeReference &type = *it; - Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); - if (type.typeData && type.typeData->isError()) { - const QString typeName = stringAt(it.key()); - - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); - return; - } - } - - // Check all composite singleton type dependencies for errors - for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { - const TypeReference &type = m_compositeSingletons.at(ii); - Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); - if (type.typeData && type.typeData->isError()) { - QString typeName = type.type.qmlTypeName(); - - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); - return; - } - } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; - { - QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); - if (error.isSet()) { - setError(error); - return; - } - } - - QQmlEngine *const engine = typeLoader()->engine(); - - const auto dependencyHasher = [engine, &resolvedTypeCache, this](QCryptographicHash *hash) { - if (!resolvedTypeCache.addToHash(hash, engine)) - return false; - return ::addTypeReferenceChecksumsToHash(m_compositeSingletons, hash, engine); - }; - - // 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_backupSourceCode = SourceCodeData(); - m_compiledData = nullptr; - } - - if (!m_document.isNull()) { - // Compile component - compile(typeNameCache, &resolvedTypeCache, dependencyHasher); - } else { - createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); - } - - if (isError()) - return; - - { - QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); - { - // Sanity check property bindings - QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); - QVector<QQmlCompileError> errors = validator.validate(); - if (!errors.isEmpty()) { - setError(errors); - return; - } - } - - m_compiledData->finalizeCompositeType(enginePrivate); - } - - { - QQmlType type = QQmlMetaType::qmlType(finalUrl(), true); - if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) { - if (!type.isValid()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); - setError(error); - return; - } else if (!type.isCompositeSingleton()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName())); - setError(error); - return; - } - } else { - // If the type is CompositeSingleton but there was no pragma Singleton in the - // QML file, lets report an error. - if (type.isValid() && type.isCompositeSingleton()) { - QString typeName = type.qmlTypeName(); - setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); - return; - } - } - } - - { - // Collect imported scripts - m_compiledData->dependentScripts.reserve(m_scripts.count()); - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { - const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); - - QStringRef qualifier(&script.qualifier); - QString enclosingNamespace; - - const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); - if (lastDotIndex != -1) { - enclosingNamespace = qualifier.left(lastDotIndex).toString(); - qualifier = qualifier.mid(lastDotIndex+1); - } - - m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); - QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); - m_compiledData->dependentScripts << scriptData; - } - } -} - -void QQmlTypeData::completed() -{ - // Notify callbacks - while (!m_callbacks.isEmpty()) { - TypeDataCallback *callback = m_callbacks.takeFirst(); - callback->typeDataReady(this); - } -} - -bool QQmlTypeData::loadImplicitImport() -{ - m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error) - - 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; - m_importCache.addImplicitImport(importDatabase, &implicitImportErrors); - - if (!implicitImportErrors.isEmpty()) { - setError(implicitImportErrors); - return false; - } - - return true; -} - -void QQmlTypeData::dataReceived(const SourceCodeData &data) -{ - m_backupSourceCode = data; - - if (tryLoadFromDiskCache()) - return; - - if (isError()) - return; - - if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { - if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) - setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); - else if (!m_backupSourceCode.exists()) - setError(QQmlTypeLoader::tr("No such file or directory")); - else - setError(QQmlTypeLoader::tr("File is empty")); - return; - } - - if (!loadFromSource()) - return; - - continueLoadFromIR(); -} - -void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) -{ - m_document.reset(new QmlIR::Document(isDebugging())); - QmlIR::IRLoader loader(unit, m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit)); - continueLoadFromIR(); -} - -bool QQmlTypeData::loadFromSource() -{ - m_document.reset(new QmlIR::Document(isDebugging())); - m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); - QQmlEngine *qmlEngine = typeLoader()->engine(); - QmlIR::IRBuilder compiler(qmlEngine->handle()->v8Engine->illegalNames()); - - QString sourceError; - const QString source = m_backupSourceCode.readAll(&sourceError); - if (!sourceError.isEmpty()) { - setError(sourceError); - return false; - } - - if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { - QList<QQmlError> errors; - errors.reserve(compiler.errors.count()); - for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { - QQmlError e; - e.setUrl(url()); - e.setLine(msg.loc.startLine); - e.setColumn(msg.loc.startColumn); - e.setDescription(msg.message); - errors << e; - } - setError(errors); - return false; - } - return true; -} - -void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) -{ - m_document.reset(new QmlIR::Document(isDebugging())); - QmlIR::IRLoader loader(unit->unitData(), m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = unit; - continueLoadFromIR(); -} - -void QQmlTypeData::continueLoadFromIR() -{ - m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); - m_importCache.setBaseUrl(finalUrl(), finalUrlString()); - - // For remote URLs, we don't delay the loading of the implicit import - // because the loading probably requires an asynchronous fetch of the - // qmldir (so we can't load it just in time). - if (!finalUrl().scheme().isEmpty()) { - QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); - if (!QQmlImports::isLocal(qmldirUrl)) { - if (!loadImplicitImport()) - return; - // This qmldir is for the implicit import - QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool(); - auto implicitImport = pool->New<QV4::CompiledData::Import>(); - implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); - implicitImport->qualifierIndex = 0; // empty string - implicitImport->majorVersion = -1; - implicitImport->minorVersion = -1; - QList<QQmlError> errors; - - if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { - setError(errors); - return; - } - } - } - - QList<QQmlError> errors; - - for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) { - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return; - } - } -} - -void QQmlTypeData::allDependenciesDone() -{ - QQmlTypeLoader::Blob::allDependenciesDone(); - - if (!m_typesResolved) { - // Check that all imports were resolved - QList<QQmlError> errors; - QHash<const QV4::CompiledData::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd(); - for ( ; it != end; ++it) { - if (*it == 0) { - // This import was not resolved - for (auto keyIt = m_unresolvedImports.keyBegin(), - keyEnd = m_unresolvedImports.keyEnd(); - keyIt != keyEnd; ++keyIt) { - const QV4::CompiledData::Import *import = *keyIt; - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(stringAt(import->uriIndex))); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); - } - } - } - if (errors.size()) { - setError(errors); - return; - } - - resolveTypes(); - m_typesResolved = true; - } -} - -void QQmlTypeData::downloadProgressChanged(qreal p) -{ - for (int ii = 0; ii < m_callbacks.count(); ++ii) { - TypeDataCallback *callback = m_callbacks.at(ii); - callback->typeDataProgress(this, p); - } -} - -QString QQmlTypeData::stringAt(int index) const -{ - if (m_compiledData) - return m_compiledData->stringAt(index); - return m_document->jsGenerator.stringTable.stringForIndex(index); -} - -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher) -{ - Q_ASSERT(m_compiledData.isNull()); - - const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && 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) { - setError(compiler.compilationErrors()); - return; - } - - const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; - if (trySaveToDisk) { - QString errorString; - if (m_compiledData->saveToDisk(url(), &errorString)) { - QString error; - if (!m_compiledData->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; - } - } -} - -void QQmlTypeData::resolveTypes() -{ - // Add any imported scripts to our resolved set - const auto resolvedScripts = m_importCache.resolvedScripts(); - for (const QQmlImports::ScriptReference &script : resolvedScripts) { - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); - addDependency(blob.data()); - - ScriptReference ref; - //ref.location = ... - if (!script.qualifier.isEmpty()) - { - ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; - // Add a reference to the enclosing namespace - m_namespaces.insert(script.qualifier); - } else { - ref.qualifier = script.nameSpace; - } - - ref.script = blob; - m_scripts << ref; - } - - // Lets handle resolved composite singleton types - const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons(); - for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) { - TypeReference ref; - QString typeName; - if (!csRef.prefix.isEmpty()) { - typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; - // Add a reference to the enclosing namespace - m_namespaces.insert(csRef.prefix); - } else { - typeName = csRef.typeName; - } - - int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; - int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; - - if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true, - QQmlType::CompositeSingletonType)) - return; - - if (ref.type.isCompositeSingleton()) { - ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); - if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies) { - // TODO: give an error message? If so, we should record and show the path of the cycle. - continue; - } - addDependency(ref.typeData.data()); - ref.prefix = csRef.prefix; - - m_compositeSingletons << ref; - } - } - - for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); - unresolvedRef != end; ++unresolvedRef) { - - TypeReference ref; // resolved reference - - const bool reportErrors = unresolvedRef->errorWhenNotFound; - - int majorVersion = -1; - int minorVersion = -1; - - const QString name = stringAt(unresolvedRef.key()); - - if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, - unresolvedRef->location.column, reportErrors, - QQmlType::AnyRegistrationType) && reportErrors) - return; - - if (ref.type.isComposite()) { - ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); - addDependency(ref.typeData.data()); - } - ref.majorVersion = majorVersion; - ref.minorVersion = minorVersion; - - ref.location.line = unresolvedRef->location.line; - ref.location.column = unresolvedRef->location.column; - - ref.needsCreation = unresolvedRef->needsCreation; - - m_resolvedTypes.insert(unresolvedRef.key(), ref); - } - - // ### this allows enums to work without explicit import or instantiation of the type - if (!m_implicitImportLoaded) - loadImplicitImport(); -} - -QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache - ) const -{ - typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); - - for (const QString &ns: m_namespaces) - (*typeNameCache)->add(ns); - - // Add any Composite Singletons that were used to the import cache - for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); - - m_importCache.populateCache(typeNameCache->data()); - - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); - - for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { - QScopedPointer<QV4::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::ResolvedTypeReference); - QQmlType qmlType = resolvedType->type; - if (resolvedType->typeData) { - if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) { - return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); - } - ref->compilationUnit = resolvedType->typeData->compilationUnit(); - } else if (qmlType.isValid()) { - ref->type = qmlType; - Q_ASSERT(ref->type.isValid()); - - if (resolvedType->needsCreation && !ref->type.isCreatable()) { - QString reason = ref->type.noCreationReason(); - if (reason.isEmpty()) - reason = tr("Element is not creatable."); - return QQmlCompileError(resolvedType->location, reason); - } - - if (ref->type.containsRevisionedAttributes()) { - ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion); - } - } - ref->majorVersion = resolvedType->majorVersion; - ref->minorVersion = resolvedType->minorVersion; - ref->doDynamicTypeCheck(); - resolvedTypeCache->insert(resolvedType.key(), ref.take()); - } - QQmlCompileError noError; - return noError; -} - -bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, - TypeReference &ref, int lineNumber, int columnNumber, - bool reportErrors, QQmlType::RegistrationType registrationType) -{ - QQmlImportNamespace *typeNamespace = nullptr; - QList<QQmlError> errors; - - bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); - 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, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); - } else { - return false; //loadImplicitImport() hit an error, and called setError already - } - } - - if ((!typeFound || typeNamespace) && reportErrors) { - // Known to not be a type: - // - known to be a namespace (Namespace {}) - // - type with unknown namespace (UnknownNamespace.SomeType {}) - QQmlError error; - if (typeNamespace) { - error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName)); - } else { - if (errors.size()) { - error = errors.takeFirst(); - } else { - // this should not be possible! - // Description should come from error provided by addImport() function. - error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database")); - } - error.setUrl(m_importCache.baseUrl()); - error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description())); - } - - if (lineNumber != -1) - error.setLine(lineNumber); - if (columnNumber != -1) - error.setColumn(columnNumber); - - errors.prepend(error); - setError(errors); - return false; - } - - return true; -} - -void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) -{ - ScriptReference ref; - ref.script = blob; - ref.location = location; - ref.qualifier = qualifier; - - m_scripts << ref; -} - -QQmlScriptData::QQmlScriptData() - : typeNameCache(nullptr) - , m_loaded(false) -{ -} - -QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData) -{ - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); - - if (m_precompiledScript->isESModule()) - return nullptr; - - auto qmlContextData = new QQmlContextData(); - - qmlContextData->isInternal = true; - qmlContextData->isJSContext = true; - if (m_precompiledScript->isSharedLibrary()) - qmlContextData->isPragmaLibraryContext = true; - else - qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext; - qmlContextData->baseUrl = url; - qmlContextData->baseUrlString = urlString; - - // For backward compatibility, if there are no imports, we need to use the - // imports from the parent context. See QTBUG-17518. - if (!typeNameCache->isEmpty()) { - qmlContextData->imports = typeNameCache; - } else if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->imports = parentQmlContextData->imports; - qmlContextData->importedScripts = parentQmlContextData->importedScripts; - } - - if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->setParent(parentQmlContextData); - } else { - qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620 - } - - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); - QV4::Scope scope(v4); - QV4::ScopedObject scriptsArray(scope); - if (qmlContextData->importedScripts.isNullOrUndefined()) { - scriptsArray = v4->newArrayObject(scripts.count()); - qmlContextData->importedScripts.set(v4, scriptsArray); - } else { - scriptsArray = qmlContextData->importedScripts.valueRef(); - } - QV4::ScopedValue v(scope); - for (int ii = 0; ii < scripts.count(); ++ii) - scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData))); - - return qmlContextData; -} - -QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData) -{ - if (m_loaded) - return m_value.value(); - - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); - QV4::Scope scope(v4); - - if (!hasEngine()) { - addToEngine(parentQmlContextData->engine); - addref(); - } - - QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData); - QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope); - if (qmlContextData) - qmlExecutionContext = - QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr); - - QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4)); - if (module) { - if (qmlContextData) { - module->d()->scope->outer.set(v4, qmlExecutionContext->d()); - qmlExecutionContext->d()->qml()->module.set(v4, module->d()); - } - - module->evaluate(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - if (error.isValid()) - QQmlEnginePrivate::get(v4)->warning(error); - } - - QV4::ScopedValue value(scope); - if (qmlContextData) - value = qmlExecutionContext->d()->qml(); - else if (module) - value = module->d(); - - if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) { - m_loaded = true; - m_value.set(v4, value); - } - - return value->asReturnedValue(); -} - -void QQmlScriptData::clear() -{ - if (typeNameCache) { - typeNameCache->release(); - typeNameCache = nullptr; - } - - scripts.clear(); - - // An addref() was made when the QQmlCleanup was added to the engine. - release(); -} - -QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) - : QQmlTypeLoader::Blob(url, JavaScriptFile, loader) - , m_isModule(url.path().endsWith(QLatin1String(".mjs"))) -{ -} - -QQmlScriptBlob::~QQmlScriptBlob() -{ -} - -QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const -{ - return m_scriptData; -} - -void QQmlScriptBlob::dataReceived(const SourceCodeData &data) -{ - if (!disableDiskCache() || forceDiskCache()) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - QString error; - if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { - initializeFromCompilationUnit(unit); - return; - } else { - qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; - } - } - - if (!data.exists()) { - if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) - setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); - else - setError(QQmlTypeLoader::tr("No such file or directory")); - return; - } - - QString error; - QString source = data.readAll(&error); - if (!error.isEmpty()) { - setError(error); - return; - } - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit; - - if (m_isModule) { - QList<QQmlJS::DiagnosticMessage> diagnostics; - unit = QV4::ExecutionEngine::compileModule(isDebugging(), urlString(), source, data.sourceTimeStamp(), &diagnostics); - QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics); - if (!errors.isEmpty()) { - setError(errors); - return; - } - } else { - QmlIR::Document irUnit(isDebugging()); - - irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); - - QmlIR::ScriptDirectivesCollector collector(&irUnit); - irUnit.jsParserEngine.setDirectives(&collector); - - QList<QQmlError> errors; - unit = QV4::Script::precompile( - &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), - source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML); - // No need to addref on unit, it's initial refcount is 1 - source.clear(); - if (!errors.isEmpty()) { - setError(errors); - return; - } - if (!unit) { - unit.adopt(new QV4::CompiledData::CompilationUnit); - } - irUnit.javaScriptCompilationUnit = unit; - - QmlIR::QmlUnitGenerator qmlGenerator; - qmlGenerator.generate(irUnit); - } - - if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) { - QString errorString; - if (unit->saveToDisk(url(), &errorString)) { - QString 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" << unit->fileName() << "to disk:" << errorString; - } - } - - initializeFromCompilationUnit(unit); -} - -void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) -{ - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - compilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString())); - initializeFromCompilationUnit(compilationUnit); -} - -void QQmlScriptBlob::done() -{ - if (isError()) - return; - - // Check all script dependencies for errors - for (int ii = 0; ii < m_scripts.count(); ++ii) { - const ScriptReference &script = m_scripts.at(ii); - Q_ASSERT(script.script->isCompleteOrError()); - if (script.script->isError()) { - QList<QQmlError> errors = script.script->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(script.location.line); - error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); - errors.prepend(error); - setError(errors); - return; - } - } - - if (!m_isModule) { - m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); - - QSet<QString> ns; - - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { - const ScriptReference &script = m_scripts.at(scriptIndex); - - m_scriptData->scripts.append(script.script); - - if (!script.nameSpace.isNull()) { - if (!ns.contains(script.nameSpace)) { - ns.insert(script.nameSpace); - m_scriptData->typeNameCache->add(script.nameSpace); - } - } - m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); - } - - m_importCache.populateCache(m_scriptData->typeNameCache); - } - m_scripts.clear(); -} - -QString QQmlScriptBlob::stringAt(int index) const -{ - return m_scriptData->m_precompiledScript->stringAt(index); -} - -void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) -{ - ScriptReference ref; - ref.script = blob; - ref.location = location; - ref.qualifier = qualifier; - ref.nameSpace = nameSpace; - - m_scripts << ref; -} - -void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit) -{ - Q_ASSERT(!m_scriptData); - m_scriptData.adopt(new QQmlScriptData()); - m_scriptData->url = finalUrl(); - m_scriptData->urlString = finalUrlString(); - m_scriptData->m_precompiledScript = unit; - - m_importCache.setBaseUrl(finalUrl(), finalUrlString()); - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> 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); - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return; - } - } - } - - auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); - - v4->injectModule(unit); - - for (const QString &request: unit->moduleRequests()) { - if (v4->moduleForUrl(QUrl(request), unit.data())) - 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()); - } -} - -QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader) -: QQmlTypeLoader::Blob(url, QmldirFile, loader) -{ -} - -const QString &QQmlQmldirData::content() const -{ - return m_content; -} - -const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const -{ - QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *>::const_iterator it = - m_imports.find(blob); - if (it == m_imports.end()) - return nullptr; - return *it; -} - -void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) -{ - m_imports[blob] = import; -} - -int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const -{ - QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob); - if (it == m_priorities.end()) - return 0; - return *it; -} - -void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority) -{ - m_priorities[blob] = priority; -} - -void QQmlQmldirData::dataReceived(const SourceCodeData &data) -{ - QString error; - m_content = data.readAll(&error); - if (!error.isEmpty()) { - setError(error); - return; - } -} - -void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) -{ - Q_UNIMPLEMENTED(); -} - -QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const -{ - error->clear(); - if (hasInlineSourceCode) - return inlineSourceCode; - - QFile f(fileInfo.absoluteFilePath()); - if (!f.open(QIODevice::ReadOnly)) { - *error = f.errorString(); - return QString(); - } - - const qint64 fileSize = fileInfo.size(); - - if (uchar *mappedData = f.map(0, fileSize)) { - QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize); - f.unmap(mappedData); - return source; - } - - QByteArray data(fileSize, Qt::Uninitialized); - if (f.read(data.data(), data.length()) != data.length()) { - *error = f.errorString(); - return QString(); - } - return QString::fromUtf8(data); -} - -QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const -{ - if (hasInlineSourceCode) - return QDateTime(); - - return fileInfo.lastModified(); -} - -bool QQmlDataBlob::SourceCodeData::exists() const -{ - if (hasInlineSourceCode) - return true; - return fileInfo.exists(); -} - -bool QQmlDataBlob::SourceCodeData::isEmpty() const -{ - if (hasInlineSourceCode) - return inlineSourceCode.isEmpty(); - return fileInfo.size() == 0; -} - QT_END_NAMESPACE - -#include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 987e47222d..38a7e05961 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -51,213 +51,27 @@ // We mean it. // +#include <private/qqmldatablob_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmlmetatype_p.h> + #include <QtQml/qtqmlglobal.h> -#include <QtCore/qobject.h> -#include <QtCore/qatomic.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qcache.h> -#if QT_CONFIG(qml_network) -#include <QtNetwork/qnetworkreply.h> -#endif #include <QtQml/qqmlerror.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlfile.h> -#include <QtQml/qqmlabstracturlinterceptor.h> -#include <private/qhashedstring_p.h> -#include <private/qqmlimport_p.h> -#include <private/qqmlcleanup_p.h> -#include <private/qqmldirparser_p.h> -#include <private/qflagpointer_p.h> -#include <private/qqmlirbuilder_p.h> +#include <QtCore/qcache.h> +#include <QtCore/qmutex.h> -#include <private/qv4value_p.h> -#include <private/qv4script_p.h> +#include <memory> QT_BEGIN_NAMESPACE -class QQmlScriptData; class QQmlScriptBlob; class QQmlQmldirData; -class QQmlTypeLoader; -class QQmlComponentPrivate; class QQmlTypeData; -class QQmlTypeLoader; class QQmlExtensionInterface; class QQmlProfiler; -struct QQmlCompileError; - -namespace QmlIR { -struct Document; -} - -class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount -{ -public: - enum Status { - Null, // Prior to QQmlTypeLoader::load() - Loading, // Prior to data being received and dataReceived() being called - WaitingForDependencies, // While there are outstanding addDependency()s - ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles - Complete, // Finished - Error // Error - }; - - enum Type { //Matched in QQmlAbstractUrlInterceptor - QmlFile = QQmlAbstractUrlInterceptor::QmlFile, - JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile, - QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile - }; - - QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager); - ~QQmlDataBlob() override; - - void startLoading(); - - QQmlTypeLoader *typeLoader() const { return m_typeLoader; } - - Type type() const; - - Status status() const; - bool isNull() const; - bool isLoading() const; - bool isWaiting() const; - bool isComplete() const; - bool isError() const; - bool isCompleteOrError() const; - - qreal progress() const; - - QUrl url() const; - QString urlString() const; - QUrl finalUrl() const; - QString finalUrlString() const; - - QList<QQmlError> errors() const; - - class SourceCodeData { - public: - QString readAll(QString *error) const; - QDateTime sourceTimeStamp() const; - bool exists() const; - bool isEmpty() const; - private: - friend class QQmlDataBlob; - friend class QQmlTypeLoader; - QString inlineSourceCode; - QFileInfo fileInfo; - bool hasInlineSourceCode = false; - }; - -protected: - // Can be called from within callbacks - void setError(const QQmlError &); - void setError(const QList<QQmlError> &errors); - void setError(const QQmlCompileError &error); - void setError(const QVector<QQmlCompileError> &errors); - void setError(const QString &description); - void addDependency(QQmlDataBlob *); - - // Callbacks made in load thread - virtual void dataReceived(const SourceCodeData &) = 0; - virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0; - virtual void done(); -#if QT_CONFIG(qml_network) - virtual void networkError(QNetworkReply::NetworkError); -#endif - virtual void dependencyError(QQmlDataBlob *); - virtual void dependencyComplete(QQmlDataBlob *); - virtual void allDependenciesDone(); - - // Callbacks made in main thread - virtual void downloadProgressChanged(qreal); - virtual void completed(); - -protected: - // Manager that is currently fetching data for me - QQmlTypeLoader *m_typeLoader; - -private: - friend class QQmlTypeLoader; - friend class QQmlTypeLoaderThread; - - void tryDone(); - void cancelAllWaitingFor(); - void notifyAllWaitingOnMe(); - void notifyComplete(QQmlDataBlob *); - - struct ThreadData { - inline ThreadData(); - inline QQmlDataBlob::Status status() const; - inline void setStatus(QQmlDataBlob::Status); - inline bool isAsync() const; - inline void setIsAsync(bool); - inline quint8 progress() const; - inline void setProgress(quint8); - - private: - QAtomicInt _p; - }; - ThreadData m_data; - - // m_errors should *always* be written before the status is set to Error. - // We use the status change as a memory fence around m_errors so that locking - // isn't required. Once the status is set to Error (or Complete), m_errors - // cannot be changed. - QList<QQmlError> m_errors; - - Type m_type; - - QUrl m_url; - QUrl m_finalUrl; - mutable QString m_urlString; - mutable QString m_finalUrlString; - - // List of QQmlDataBlob's that are waiting for me to complete. - QList<QQmlDataBlob *> m_waitingOnMe; - - // List of QQmlDataBlob's that I am waiting for to complete. - QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor; - - int m_redirectCount:30; - bool m_inCallback:1; - bool m_isDone:1; -}; - class QQmlTypeLoaderThread; - -class QQmlTypeLoaderQmldirContent -{ -private: - friend class QQmlTypeLoader; - - void setContent(const QString &location, const QString &content); - void setError(const QQmlError &); - -public: - QQmlTypeLoaderQmldirContent(); - QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; - QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; - - bool hasContent() const { return m_hasContent; } - bool hasError() const; - QList<QQmlError> errors(const QString &uri) const; - - QString typeNamespace() const; - - QQmlDirComponents components() const; - QQmlDirScripts scripts() const; - QQmlDirPlugins plugins() const; - - QString pluginLocation() const; - - bool designerSupported() const; - -private: - QQmlDirParser m_parser; - QString m_location; - bool m_hasContent = false; -}; +class QQmlEngine; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader { @@ -275,11 +89,31 @@ public: void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + struct PendingImport + { + QV4::CompiledData::Import::ImportType type = QV4::CompiledData::Import::ImportType::ImportLibrary; + + QString uri; + QString qualifier; + + int majorVersion = -1; + int minorVersion = -1; + + QV4::CompiledData::Location location; + + int priority = 0; + + PendingImport() = default; + PendingImport(Blob *blob, const QV4::CompiledData::Import *import); + }; + using PendingImportPtr = std::shared_ptr<PendingImport>; + protected: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); + bool addImport(PendingImportPtr import, QList<QQmlError> *errors); - bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors); - bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors); + bool fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors); + bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors); private: virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *); @@ -288,13 +122,16 @@ public: void dependencyComplete(QQmlDataBlob *) override; + bool loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors); + protected: virtual QString stringAt(int) const { return QString(); } bool isDebugging() const; + bool diskCacheEnabled() const; QQmlImports m_importCache; - QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; + QVector<PendingImportPtr> m_unresolvedImports; QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs; QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; @@ -418,209 +255,6 @@ private: friend struct StaticLoader; }; -class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob -{ - Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) -public: - struct TypeReference - { - TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {} - - QV4::CompiledData::Location location; - QQmlType type; - int majorVersion; - int minorVersion; - QQmlRefPointer<QQmlTypeData> typeData; - QString prefix; // used by CompositeSingleton types - QString qualifiedName() const; - bool needsCreation; - }; - - struct ScriptReference - { - QV4::CompiledData::Location location; - QString qualifier; - QQmlRefPointer<QQmlScriptBlob> script; - }; - -private: - friend class QQmlTypeLoader; - - QQmlTypeData(const QUrl &, QQmlTypeLoader *); - -public: - ~QQmlTypeData() override; - - const QList<ScriptReference> &resolvedScripts() const; - - QV4::CompiledData::CompilationUnit *compilationUnit() const; - - // Used by QQmlComponent to get notifications - struct TypeDataCallback { - virtual ~TypeDataCallback(); - virtual void typeDataProgress(QQmlTypeData *, qreal) {} - virtual void typeDataReady(QQmlTypeData *) {} - }; - void registerCallback(TypeDataCallback *); - void unregisterCallback(TypeDataCallback *); - -protected: - void done() override; - void completed() override; - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; - void allDependenciesDone() override; - void downloadProgressChanged(qreal) override; - - QString stringAt(int index) const override; - -private: - bool tryLoadFromDiskCache(); - bool loadFromSource(); - void restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit); - void continueLoadFromIR(); - void resolveTypes(); - QQmlCompileError buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache - ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); - bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, - TypeReference &ref, int lineNumber = -1, int columnNumber = -1, - bool reportErrors = true, - QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType); - - void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - - - SourceCodeData m_backupSourceCode; // used when cache verification fails. - QScopedPointer<QmlIR::Document> m_document; - QV4::CompiledData::TypeReferenceMap m_typeReferences; - - QList<ScriptReference> m_scripts; - - QSet<QString> m_namespaces; - QList<TypeReference> m_compositeSingletons; - - // map from name index to resolved type - // While this could be a hash, a map is chosen here to provide a stable - // order, which is used to calculating a check-sum on dependent meta-objects. - QMap<int, TypeReference> m_resolvedTypes; - bool m_typesResolved:1; - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compiledData; - - QList<TypeDataCallback *> m_callbacks; - - bool m_implicitImportLoaded; - bool loadImplicitImport(); -}; - -// QQmlScriptData instances are created, uninitialized, by the loader in the -// load thread. The first time they are used by the VME, they are initialized which -// creates their v8 objects and they are referenced and added to the engine's cleanup -// list. During QQmlCleanup::clear() all v8 resources are destroyed, and the -// reference that was created is released but final deletion only occurs once all the -// references as released. This is all intended to ensure that the v8 resources are -// only created and destroyed in the main thread :) -class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount -{ -private: - friend class QQmlTypeLoader; - - QQmlScriptData(); - -public: - QUrl url; - QString urlString; - QQmlTypeNameCache *typeNameCache; - QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; - - QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() const { return m_precompiledScript; } - -protected: - void clear() override; // From QQmlCleanup - -private: - friend class QQmlScriptBlob; - - void initialize(QQmlEngine *); - QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData); - - bool m_loaded; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_precompiledScript; - QV4::PersistentValue m_value; -}; - -class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob -{ -private: - friend class QQmlTypeLoader; - - QQmlScriptBlob(const QUrl &, QQmlTypeLoader *); - -public: - ~QQmlScriptBlob() override; - - struct ScriptReference - { - QV4::CompiledData::Location location; - QString qualifier; - QString nameSpace; - QQmlRefPointer<QQmlScriptBlob> script; - }; - - QQmlRefPointer<QQmlScriptData> scriptData() const; - -protected: - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; - void done() override; - - QString stringAt(int index) const override; - -private: - void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - void initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit); - - QList<ScriptReference> m_scripts; - QQmlRefPointer<QQmlScriptData> m_scriptData; - const bool m_isModule; -}; - -class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob -{ -private: - friend class QQmlTypeLoader; - - QQmlQmldirData(const QUrl &, QQmlTypeLoader *); - -public: - const QString &content() const; - - const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const; - void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *); - - int priority(QQmlTypeLoader::Blob *) const; - void setPriority(QQmlTypeLoader::Blob *, int); - -protected: - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override; - -private: - QString m_content; - QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *> m_imports; - QHash<QQmlTypeLoader::Blob *, int> m_priorities; -}; - - QT_END_NAMESPACE #endif // QQMLTYPELOADER_P_H diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp new file mode 100644 index 0000000000..af97643163 --- /dev/null +++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#include <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) + : l(l) +{ +} + +void QQmlTypeLoaderNetworkReplyProxy::finished() +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + l->networkReplyFinished(reply); +} + +void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + l->networkReplyProgress(reply, bytesReceived, bytesTotal); +} + +// This function is for when you want to shortcut the signals and call directly +void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) +{ + qint64 replySize = reply->size(); + l->networkReplyProgress(reply, replySize, replySize); + l->networkReplyFinished(reply); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h new file mode 100644 index 0000000000..ed87a2b508 --- /dev/null +++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERNETWORKREPLYPROXY_P_H +#define QQMLTYPELOADERNETWORKREPLYPROXY_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/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_REQUIRE_CONFIG(qml_network); + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QQmlTypeLoader; + +// This is a lame object that we need to ensure that slots connected to +// QNetworkReply get called in the correct thread (the loader thread). +// As QQmlTypeLoader lives in the main thread, and we can't use +// Qt::DirectConnection connections from a QNetworkReply (because then +// sender() wont work), we need to insert this object in the middle. +class QQmlTypeLoaderNetworkReplyProxy : public QObject +{ + Q_OBJECT +public: + QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l); + +public slots: + void finished(); + void downloadProgress(qint64, qint64); + void manualFinished(QNetworkReply*); + +private: + QQmlTypeLoader *l; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERNETWORKREPLYPROXY_P_H diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp new file mode 100644 index 0000000000..8e983db756 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypeloaderqmldircontent_p.h> +#include <QtQml/qqmlerror.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() +{ +} + +bool QQmlTypeLoaderQmldirContent::hasError() const +{ + return m_parser.hasError(); +} + +QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const +{ + QList<QQmlError> errors; + const QUrl url(uri); + const auto parseErrors = m_parser.errors(uri); + for (const auto &parseError : parseErrors) { + QQmlError error; + error.setUrl(url); + error.setLine(parseError.line); + error.setColumn(parseError.column); + error.setDescription(parseError.message); + errors.append(error); + } + return errors; +} + +QString QQmlTypeLoaderQmldirContent::typeNamespace() const +{ + return m_parser.typeNamespace(); +} + +void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) +{ + m_hasContent = true; + m_location = location; + m_parser.parse(content); +} + +void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) +{ + QQmlJS::DiagnosticMessage parseError; + parseError.line = error.line(); + parseError.column = error.column(); + parseError.message = error.description(); + m_parser.setError(parseError); +} + +QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const +{ + return m_parser.components(); +} + +QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const +{ + return m_parser.scripts(); +} + +QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const +{ + return m_parser.plugins(); +} + +QStringList QQmlTypeLoaderQmldirContent::imports() const +{ + return m_parser.imports(); +} + +QString QQmlTypeLoaderQmldirContent::pluginLocation() const +{ + return m_location; +} + +bool QQmlTypeLoaderQmldirContent::designerSupported() const +{ + return m_parser.designerSupported(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h new file mode 100644 index 0000000000..698643c7ec --- /dev/null +++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERQMLDIRCONTENT_P_H +#define QQMLTYPELOADERQMLDIRCONTENT_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 <private/qqmldirparser_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlError; +class QQmlTypeLoaderQmldirContent +{ +private: + friend class QQmlTypeLoader; + + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); + +public: + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; + + bool hasContent() const { return m_hasContent; } + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + QString typeNamespace() const; + + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + QStringList imports() const; + + QString pluginLocation() const; + + bool designerSupported() const; + +private: + QQmlDirParser m_parser; + QString m_location; + bool m_hasContent = false; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERQMLDIRCONTENT_P_H diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp new file mode 100644 index 0000000000..0e1cecd1e5 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderthread.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlengine_p.h> +#include <private/qqmlextensionplugin_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#if QT_CONFIG(qml_network) +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#endif + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) + : m_loader(loader) +#if QT_CONFIG(qml_network) + , m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr) +#endif // qml_network +{ + // Do that after initializing all the members. + startup(); +} + +#if QT_CONFIG(qml_network) +QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const +{ + Q_ASSERT(isThisThread()); + if (!m_networkAccessManager) { + m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr); + m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader); + } + + return m_networkAccessManager; +} + +QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const +{ + Q_ASSERT(isThisThread()); + Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first + return m_networkReplyProxy; +} +#endif // qml_network + +void QQmlTypeLoaderThread::load(QQmlDataBlob *b) +{ + b->addref(); + callMethodInThread(&This::loadThread, b); +} + +void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b) +{ + b->addref(); + postMethodToThread(&This::loadThread, b); +} + +void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + callMethodInThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + postMethodToThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + b->addref(); + callMethodInThread(&This::loadWithCachedUnitThread, b, unit); +} + +void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + b->addref(); + postMethodToThread(&This::loadWithCachedUnitThread, b, unit); +} + +void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b) +{ + b->addref(); +#if !QT_CONFIG(thread) + if (!isThisThread()) + postMethodToThread(&This::callCompletedMain, b); +#else + postMethodToMain(&This::callCompletedMain, b); +#endif +} + +void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) +{ + b->addref(); +#if !QT_CONFIG(thread) + if (!isThisThread()) + postMethodToThread(&This::callDownloadProgressChangedMain, b, p); +#else + postMethodToMain(&This::callDownloadProgressChangedMain, b, p); +#endif +} + +void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, + const char *uri) +{ + callMethodInMain(&This::initializeEngineMain, iface, uri); +} + +void QQmlTypeLoaderThread::shutdownThread() +{ +#if QT_CONFIG(qml_network) + delete m_networkAccessManager; + m_networkAccessManager = nullptr; + delete m_networkReplyProxy; + m_networkReplyProxy = nullptr; +#endif // qml_network +} + +void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) +{ + m_loader->loadThread(b); + b->release(); +} + +void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d) +{ + m_loader->loadWithStaticDataThread(b, d); + b->release(); +} + +void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + m_loader->loadWithCachedUnitThread(b, unit); + b->release(); +} + +void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString())); +#endif + b->completed(); + b->release(); +} + +void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback", + qPrintable(b->urlString()), p); +#endif + b->downloadProgressChanged(p); + b->release(); +} + +void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, + const char *uri) +{ + Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); + iface->initializeEngine(m_loader->engine(), uri); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloaderthread_p.h b/src/qml/qml/qqmltypeloaderthread_p.h new file mode 100644 index 0000000000..67e47e86de --- /dev/null +++ b/src/qml/qml/qqmltypeloaderthread_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERTHREAD_P_H +#define QQMLTYPELOADERTHREAD_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 <private/qqmlthread_p.h> +#include <private/qv4compileddata_p.h> + +#include <QtQml/qtqmlglobal.h> + +#if QT_CONFIG(qml_network) +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQmlDataBlob; +class QQmlTypeLoader; +class QQmlExtensionInterface; + +class QQmlTypeLoaderThread : public QQmlThread +{ + typedef QQmlTypeLoaderThread This; + +public: + QQmlTypeLoaderThread(QQmlTypeLoader *loader); +#if QT_CONFIG(qml_network) + QNetworkAccessManager *networkAccessManager() const; + QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; +#endif // qml_network + void load(QQmlDataBlob *b); + void loadAsync(QQmlDataBlob *b); + void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); + void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &); + void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void callCompleted(QQmlDataBlob *b); + void callDownloadProgressChanged(QQmlDataBlob *b, qreal p); + void initializeEngine(QQmlExtensionInterface *, const char *); + +protected: + void shutdownThread() override; + +private: + void loadThread(QQmlDataBlob *b); + void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &); + void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void callCompletedMain(QQmlDataBlob *b); + void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p); + void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); + + QQmlTypeLoader *m_loader; +#if QT_CONFIG(qml_network) + mutable QNetworkAccessManager *m_networkAccessManager; + mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; +#endif // qml_network +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERTHREAD_P_H diff --git a/src/qml/qml/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp new file mode 100644 index 0000000000..9c9bf3e48f --- /dev/null +++ b/src/qml/qml/qqmltypemodule.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypemodule_p_p.h" + +#include <private/qqmltype_p_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeModule::QQmlTypeModule(const QString &module, int majorVersion) + : d(new QQmlTypeModulePrivate(module, majorVersion)) +{ +} + +QQmlTypeModule::~QQmlTypeModule() +{ + delete d; +} + +QString QQmlTypeModule::module() const +{ + // No need to lock. d->module is const + return d->module; +} + +int QQmlTypeModule::majorVersion() const +{ + // No need to lock. d->majorVersion is const + return d->majorVersion; +} + +int QQmlTypeModule::minimumMinorVersion() const +{ + return d->minMinorVersion.loadRelaxed(); +} + +int QQmlTypeModule::maximumMinorVersion() const +{ + return d->maxMinorVersion.loadRelaxed(); +} + +void QQmlTypeModule::addMinorVersion(int version) +{ + for (int oldVersion = d->minMinorVersion.loadRelaxed(); + oldVersion > version && !d->minMinorVersion.testAndSetOrdered(oldVersion, version); + oldVersion = d->minMinorVersion.loadRelaxed()) { + } + + for (int oldVersion = d->maxMinorVersion.loadRelaxed(); + oldVersion < version && !d->maxMinorVersion.testAndSetOrdered(oldVersion, version); + oldVersion = d->maxMinorVersion.loadRelaxed()) { + } +} + +void QQmlTypeModule::add(QQmlTypePrivate *type) +{ + QMutexLocker lock(&d->mutex); + addMinorVersion(type->version_min); + + QList<QQmlTypePrivate *> &list = d->typeHash[type->elementName]; + for (int ii = 0; ii < list.count(); ++ii) { + Q_ASSERT(list.at(ii)); + if (list.at(ii)->version_min < type->version_min) { + list.insert(ii, type); + return; + } + } + list.append(type); +} + +void QQmlTypeModule::remove(const QQmlTypePrivate *type) +{ + QMutexLocker lock(&d->mutex); + for (auto elementIt = d->typeHash.begin(); elementIt != d->typeHash.end();) { + QQmlMetaType::removeQQmlTypePrivate(elementIt.value(), type); + +#if 0 + if (list.isEmpty()) + elementIt = typeHash.erase(elementIt); + else + ++elementIt; +#else + ++elementIt; +#endif + } +} + +bool QQmlTypeModule::isLocked() const +{ + return d->locked.loadRelaxed() != 0; +} + +void QQmlTypeModule::lock() +{ + d->locked.storeRelaxed(1); +} + +QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const +{ + QMutexLocker lock(&d->mutex); + QList<QQmlTypePrivate *> *types = d->typeHash.value(name); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); + } + + return QQmlType(); +} + +QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const +{ + QMutexLocker lock(&d->mutex); + QList<QQmlTypePrivate *> *types = d->typeHash.value(name); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); + } + + return QQmlType(); +} + +void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const +{ + QMutexLocker lock(&d->mutex); + for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end(); + typeCandidates != end; ++typeCandidates) { + for (auto type: typeCandidates.value()) { + if (type->regType == QQmlType::CompositeSingletonType) + callback(QQmlType(type)); + } + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h new file mode 100644 index 0000000000..b84a91b5db --- /dev/null +++ b/src/qml/qml/qqmltypemodule_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPEMODULE_P_H +#define QQMLTYPEMODULE_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/qtqmlglobal.h> +#include <QtCore/qstring.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +class QQmlType; +class QQmlTypePrivate; +struct QQmlMetaTypeData; +class QHashedString; +class QHashedStringRef; + +namespace QV4 { +struct String; +} + +class QQmlTypeModulePrivate; +class QQmlTypeModule +{ +public: + QQmlTypeModule(const QString &uri = QString(), int majorVersion = 0); + ~QQmlTypeModule(); + + void add(QQmlTypePrivate *); + void remove(const QQmlTypePrivate *type); + + bool isLocked() const; + void lock(); + + QString module() const; + int majorVersion() const; + + void addMinorVersion(int minorVersion); + int minimumMinorVersion() const; + int maximumMinorVersion() const; + + QQmlType type(const QHashedStringRef &, int) const; + QQmlType type(const QV4::String *, int) const; + + void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const; + +private: + QQmlTypeModulePrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULE_P_H diff --git a/src/qml/qml/qqmltypemodule_p_p.h b/src/qml/qml/qqmltypemodule_p_p.h new file mode 100644 index 0000000000..b1dab1c4a0 --- /dev/null +++ b/src/qml/qml/qqmltypemodule_p_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPEMODULE_P_P_H +#define QQMLTYPEMODULE_P_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 <private/qqmltypemodule_p.h> +#include <private/qstringhash_p.h> +#include <private/qqmlmetatypedata_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeModulePrivate +{ +public: + QQmlTypeModulePrivate(QString module, int majorVersion) : + module(std::move(module)), majorVersion(majorVersion) + {} + + const QString module; + const int majorVersion = 0; + + // Can only ever decrease + QAtomicInt minMinorVersion = std::numeric_limits<int>::max(); + + // Can only ever increase + QAtomicInt maxMinorVersion = 0; + + // Bool. Can only be set to 1 once. + QAtomicInt locked = 0; + + typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash; + TypeHash typeHash; + + QMutex mutex; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULE_P_P_H diff --git a/src/qml/qml/qqmltypemoduleversion.cpp b/src/qml/qml/qqmltypemoduleversion.cpp new file mode 100644 index 0000000000..bbbfa1a7b6 --- /dev/null +++ b/src/qml/qml/qqmltypemoduleversion.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypemoduleversion_p.h" + +#include <private/qqmltype_p.h> +#include <private/qqmltypemodule_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeModuleVersion::QQmlTypeModuleVersion() + : m_module(nullptr), m_minor(0) +{ +} + +QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor) + : m_module(module), m_minor(minor) +{ + Q_ASSERT(m_module); + Q_ASSERT(m_minor >= 0); +} + +QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o) + : m_module(o.m_module), m_minor(o.m_minor) +{ +} + +QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o) +{ + m_module = o.m_module; + m_minor = o.m_minor; + return *this; +} + +QQmlTypeModule *QQmlTypeModuleVersion::module() const +{ + return m_module; +} + +int QQmlTypeModuleVersion::minorVersion() const +{ + return m_minor; +} + +QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const +{ + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); +} + +QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const +{ + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypemoduleversion_p.h b/src/qml/qml/qqmltypemoduleversion_p.h new file mode 100644 index 0000000000..20f4709ecb --- /dev/null +++ b/src/qml/qml/qqmltypemoduleversion_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPEMODULEVERSION_P_H +#define QQMLTYPEMODULEVERSION_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/qtqmlglobal.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeModule; +class QQmlType; +class QHashedStringRef; + +namespace QV4 { +struct String; +} + +class QQmlTypeModuleVersion +{ +public: + QQmlTypeModuleVersion(); + QQmlTypeModuleVersion(QQmlTypeModule *, int); + QQmlTypeModuleVersion(const QQmlTypeModuleVersion &); + QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &); + + QQmlTypeModule *module() const; + int minorVersion() const; + + QQmlType type(const QHashedStringRef &) const; + QQmlType type(const QV4::String *) const; + +private: + QQmlTypeModule *m_module; + int m_minor; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULEVERSION_P_H diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 28b5e7f0ad..b98fe77ed5 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -55,8 +55,9 @@ #include "qqmlcleanup_p.h" #include "qqmlmetatype_p.h" -#include <private/qhashedstring_p.h> +#include <private/qstringhash_p.h> #include <private/qqmlimport_p.h> +#include <private/qqmltypemoduleversion_p.h> #include <QtCore/qvector.h> diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index c5fa4a04ec..ef4a628a04 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -38,10 +38,11 @@ ****************************************************************************/ #include "qqmltypewrapper_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlcontext_p.h> +#include <private/qqmlmetaobject_p.h> +#include <private/qqmltypedata_p.h> #include <private/qjsvalue_p.h> #include <private/qv4functionobject_p.h> @@ -89,10 +90,8 @@ QObject* QQmlTypeWrapper::singletonObject() const if (!isSingleton()) return nullptr; - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - return siinfo->qobjectApi(e); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + return e->singletonInstance<QObject*>(d()->type()); } QVariant QQmlTypeWrapper::toVariant() const @@ -100,13 +99,12 @@ QVariant QQmlTypeWrapper::toVariant() const if (!isSingleton()) return QVariant::fromValue<QObject *>(d()->object); - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) - return QVariant::fromValue<QObject*>(qobjectSingleton); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + const QQmlType type = d()->type(); + if (type.isQJSValueSingleton()) + return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type)); - return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e)); + return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type)); } @@ -194,50 +192,51 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // singleton types are handled differently to other types. if (type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - // check for enum value - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (includeEnums && name->startsWithUpper()) { - bool ok = false; - int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return QV4::Value::fromInt32(value).asReturnedValue(); - - value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); - if (ok) { - Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); - enumWrapper->d()->typePrivate = type.priv(); - QQmlType::refHandle(enumWrapper->d()->typePrivate); - enumWrapper->d()->scopeEnumIndex = value; - return enumWrapper.asReturnedValue(); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + QJSValue scriptSingleton; + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + // check for enum value + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (includeEnums && name->startsWithUpper()) { + bool ok = false; + int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return QV4::Value::fromInt32(value).asReturnedValue(); + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + return enumWrapper.asReturnedValue(); + } } - } - // check for property. - bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); - if (hasProperty) - *hasProperty = ok; - - // Warn when attempting to access a lowercased enum value, singleton case - if (!ok && includeEnums && !name->startsWithUpper()) { - enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return throwLowercaseEnumError(v4, name, type); - } + // check for property. + bool ok; + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + if (hasProperty) + *hasProperty = ok; + + // Warn when attempting to access a lowercased enum value, singleton case + if (!ok && includeEnums && !name->startsWithUpper()) { + enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return throwLowercaseEnumError(v4, name, type); + } - return result; - } else if (!siinfo->scriptApi(e).isUndefined()) { - // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); - if (!!o) - return o->get(name); + return result; + } + } else if (type.isQJSValueSingleton()) { + QJSValue scriptSingleton = e->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::convertedToValue(v4, scriptSingleton)); + if (!!o) + return o->get(name); + } } // Fall through to base implementation @@ -342,21 +341,22 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); return false; } else if (type.isSingleton()) { - QQmlEngine *e = scope.engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); - } else if (!siinfo->scriptApi(e).isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e))); - if (!apiprivate) { - QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - scope.engine->throwError(error); - return false; - } else { - return apiprivate->put(name, value); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine()); + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) + return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + + } else { + QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + if (!scriptSingleton.isUndefined()) { + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton)); + if (!apiprivate) { + QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); + scope.engine->throwError(error); + return false; + } else { + return apiprivate->put(name, value); + } } } } @@ -419,7 +419,7 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return Encode(false); QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); - CompiledData::CompilationUnit *cu = td->compilationUnit(); + ExecutableCompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); @@ -448,27 +448,25 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, if (type.isValid()) { if (type.isSingleton()) { - QQmlEngine *e = engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (!includeEnums || !name->startsWithUpper()) { - QQmlData *ddata = QQmlData::get(qobjectSingleton, false); - if (ddata && ddata->propertyCache) { - QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); - if (property) { - ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); - lookup->qobjectLookup.qmlTypeIc = This->internalClass(); - lookup->qobjectLookup.ic = val->objectValue()->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = property; - lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; - return lookup->getter(lookup, engine, *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; + if (!includeEnums || !name->startsWithUpper()) { + QQmlData *ddata = QQmlData::get(qobjectSingleton, false); + if (ddata && ddata->propertyCache) { + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + if (property) { + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + lookup->qobjectLookup.qmlTypeIc = This->internalClass(); + lookup->qobjectLookup.ic = val->objectValue()->internalClass(); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + return lookup->getter(lookup, engine, *object); + } + // Fall through to base implementation } // Fall through to base implementation } @@ -478,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, } // Fall through to base implementation } + + if (name->startsWithUpper()) { + bool ok = false; + int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + lookup->qmlEnumValueLookup.ic = This->internalClass(); + lookup->qmlEnumValueLookup.encodedEnumValue + = QV4::Value::fromInt32(value).asReturnedValue(); + lookup->getter = QQmlTypeWrapper::lookupEnumValue; + return lookup->getter(lookup, engine, *object); + } + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper( + scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + + lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); + lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper + = static_cast<Heap::Object*>(enumWrapper->heapObject()); + lookup->getter = QQmlTypeWrapper::lookupScopedEnum; + return enumWrapper.asReturnedValue(); + } + // Fall through to base implementation + } // Fall through to base implementation } return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); @@ -509,22 +535,46 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin if (!type.isValid()) return revertLookup(); - if (!type.isSingleton()) + if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) return revertLookup(); - QQmlEngine *e = engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (!qobjectSingleton) - return revertLookup(); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); + Q_ASSERT(qobjectSingleton); Scope scope(engine); ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return l->qmlEnumValueLookup.encodedEnumValue; +} + +ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + Scope scope(engine); + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); + + 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->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return enumWrapper.asReturnedValue(); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 5f3cb2523f..7dc3f55310 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -81,7 +81,7 @@ struct QQmlTypeWrapper : Object { QQmlType type() const; - QQmlTypePrivate *typePrivate; + const QQmlTypePrivate *typePrivate; QQmlTypeNameCache *typeNamespace; const QQmlImportRef *importNamespace; }; @@ -90,7 +90,7 @@ struct QQmlScopedEnumWrapper : Object { void init() { Object::init(); } void destroy(); int scopeEnumIndex; - QQmlTypePrivate *typePrivate; + const QQmlTypePrivate *typePrivate; QQmlType type() const; }; @@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base); protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 6fd0f0d37c..a4737b3da9 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -38,12 +38,14 @@ ****************************************************************************/ #include "qqmlvaluetype_p.h" -#include "qqmlmetatype_p.h" +#include <QtCore/qmutex.h> #include <private/qqmlglobal_p.h> #include <QtCore/qdebug.h> #include <private/qmetaobjectbuilder_p.h> +#if QT_CONFIG(qml_itemmodel) #include <private/qqmlmodelindexvaluetype_p.h> +#endif #include <private/qmetatype_p.h> QT_BEGIN_NAMESPACE @@ -63,32 +65,34 @@ struct QQmlValueTypeFactoryImpl QQmlValueType *valueTypes[QVariant::UserType]; QHash<int, QQmlValueType *> userTypes; QMutex mutex; + + QQmlValueType invalidValueType; }; QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { - std::fill_n(valueTypes, int(QVariant::UserType), nullptr); + std::fill_n(valueTypes, int(QVariant::UserType), &invalidValueType); +#if QT_CONFIG(qml_itemmodel) // See types wrapped in qqmlmodelindexvaluetype_p.h qRegisterMetaType<QItemSelectionRange>(); +#endif } QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() { - qDeleteAll(valueTypes, valueTypes + QVariant::UserType); + for (QQmlValueType *type : valueTypes) { + if (type != &invalidValueType) + delete type; + } qDeleteAll(userTypes); } -bool QQmlValueTypeFactoryImpl::isValueType(int idx) +bool isInternalType(int idx) { - if (idx >= QMetaType::User) - return valueType(idx) != nullptr; - - if (idx < 0) - return false; - // Qt internal types switch (idx) { + case QMetaType::UnknownType: case QMetaType::QStringList: case QMetaType::QObjectStar: case QMetaType::VoidStar: @@ -97,12 +101,20 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx) case QMetaType::QLocale: case QMetaType::QImage: // scarce type, keep as QVariant case QMetaType::QPixmap: // scarce type, keep as QVariant - return false; - default: return true; + default: + return false; } } +bool QQmlValueTypeFactoryImpl::isValueType(int idx) +{ + if (idx < 0 || isInternalType(idx)) + return false; + + return valueType(idx) != nullptr; +} + const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) { switch (t) { @@ -120,13 +132,17 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) return &QQmlRectFValueType::staticMetaObject; case QVariant::EasingCurve: return &QQmlEasingValueType::staticMetaObject; +#if QT_CONFIG(qml_itemmodel) case QVariant::ModelIndex: return &QQmlModelIndexValueType::staticMetaObject; case QVariant::PersistentModelIndex: return &QQmlPersistentModelIndexValueType::staticMetaObject; +#endif default: +#if QT_CONFIG(qml_itemmodel) if (t == qMetaTypeId<QItemSelectionRange>()) return &QQmlItemSelectionRangeValueType::staticMetaObject; +#endif if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t)) return mo; @@ -158,15 +174,17 @@ QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx) } QQmlValueType *rv = valueTypes[idx]; - if (!rv) { + if (rv == &invalidValueType) { // No need for mutex protection - the most we can lose is a valueType instance // TODO: Investigate the performance/memory characteristics of // removing the preallocated array - if (const QMetaObject *mo = metaObjectForMetaType(idx)) { - rv = new QQmlValueType(idx, mo); - valueTypes[idx] = rv; - } + if (isInternalType(idx)) + rv = valueTypes[idx] = nullptr; + else if (const QMetaObject *mo = metaObjectForMetaType(idx)) + rv = valueTypes[idx] = new QQmlValueType(idx, mo); + else + rv = valueTypes[idx] = nullptr; } return rv; @@ -196,6 +214,13 @@ void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing"); } +QQmlValueType::QQmlValueType() : + _metaObject(nullptr), + gadgetPtr(nullptr), + metaType(QMetaType::UnknownType) +{ +} + QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) : gadgetPtr(QMetaType::create(typeId)) , metaType(typeId) @@ -213,7 +238,7 @@ QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) QQmlValueType::~QQmlValueType() { QObjectPrivate *op = QObjectPrivate::get(this); - Q_ASSERT(op->metaObject == this); + Q_ASSERT(op->metaObject == nullptr || op->metaObject == this); op->metaObject = nullptr; ::free(const_cast<QMetaObject *>(_metaObject)); metaType.destroy(gadgetPtr); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 89f1b71d61..cf53b8cb4a 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlValueType : public QObject, public QAbstractDynamicMetaObject { public: + QQmlValueType(); QQmlValueType(int userType, const QMetaObject *metaObject); ~QQmlValueType() override; void read(QObject *, int); @@ -90,7 +91,7 @@ public: class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory { public: - static bool isValueType(int); + static bool isValueType(int idx); static QQmlValueType *valueType(int idx); static const QMetaObject *metaObjectForMetaType(int type); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 7df5757b95..cf6553d129 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qqmlvaluetypewrapper_p.h" -#include <private/qv8engine_p.h> + #include <private/qqmlvaluetype_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlglobal_p.h> diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 6bc469c836..abdc686ec2 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -57,6 +57,8 @@ #include <private/qv4scopedvalue_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> QT_BEGIN_NAMESPACE @@ -162,8 +164,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() QQmlData *targetDData = QQmlData::get(target, /*create*/false); if (!targetDData) return; - int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex(); + QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); + int coreIndex = encodedIndex.coreIndex(); + int valueTypeIndex = encodedIndex.valueTypeIndex(); const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); + if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) { + // deep alias + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine()); + auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType()); + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + Q_ASSERT(newPropertyCache); + pd = newPropertyCache->property(valueTypeIndex); + } if (!pd) return; @@ -316,7 +329,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, - const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId) + const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), engine(engine), ctxt(QQmlData::get(obj, true)->outerContext), @@ -594,7 +607,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const QV4::Scope scope(engine); QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id)); if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) { - QVariant variant(qVariantFromValue(QList<QObject*>())); + QVariant variant(QVariant::fromValue(QList<QObject*>())); v = engine->newVariantObject(variant); md->set(engine, id, v); } @@ -638,205 +651,170 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= propOffset(); if (id < propertyCount) { - const QV4::CompiledData::Property::Type t = static_cast<QV4::CompiledData::Property::Type>(qint32(compiledObject->propertyTable()[id].type)); - bool needActivate = false; - - if (t == QV4::CompiledData::Property::Var) { - // the context can be null if accessing var properties from cpp after re-parenting an item. - QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); - if (ep) { - if (c == QMetaObject::ReadProperty) { - *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); - } else if (c == QMetaObject::WriteProperty) { - writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - } - } else if (c == QMetaObject::ReadProperty) { - // if the context was disposed, we just return an invalid variant from read. - *reinterpret_cast<QVariant *>(a[0]) = QVariant(); - } + const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id]; + const QV4::CompiledData::BuiltinType t = property.builtinType(); - } else { - int fallbackMetaType = QMetaType::UnknownType; + // the context can be null if accessing var properties from cpp after re-parenting an item. + QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); + + const int fallbackMetaType = QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(t); + + if (c == QMetaObject::ReadProperty) { switch (t) { - case QV4::CompiledData::Property::Font: - fallbackMetaType = QMetaType::QFont; + case QV4::CompiledData::BuiltinType::Int: + *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); break; - case QV4::CompiledData::Property::Time: - fallbackMetaType = QMetaType::QTime; + case QV4::CompiledData::BuiltinType::Bool: + *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); break; - case QV4::CompiledData::Property::Color: - fallbackMetaType = QMetaType::QColor; + case QV4::CompiledData::BuiltinType::Real: + *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); break; - case QV4::CompiledData::Property::Vector2D: - fallbackMetaType = QMetaType::QVector2D; + case QV4::CompiledData::BuiltinType::String: + *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); break; - case QV4::CompiledData::Property::Vector3D: - fallbackMetaType = QMetaType::QVector3D; + case QV4::CompiledData::BuiltinType::Url: + *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); break; - case QV4::CompiledData::Property::Vector4D: - fallbackMetaType = QMetaType::QVector4D; + case QV4::CompiledData::BuiltinType::Date: + *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); break; - case QV4::CompiledData::Property::Matrix4x4: - fallbackMetaType = QMetaType::QMatrix4x4; + case QV4::CompiledData::BuiltinType::DateTime: + *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); break; - case QV4::CompiledData::Property::Quaternion: - fallbackMetaType = QMetaType::QQuaternion; + case QV4::CompiledData::BuiltinType::Rect: + *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); break; - default: break; - } - - - if (c == QMetaObject::ReadProperty) { - switch (t) { - case QV4::CompiledData::Property::Int: - *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); - break; - case QV4::CompiledData::Property::Bool: - *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); - break; - case QV4::CompiledData::Property::Real: - *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); - break; - case QV4::CompiledData::Property::String: - *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); - break; - case QV4::CompiledData::Property::Url: - *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); - break; - case QV4::CompiledData::Property::Date: - *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); - break; - case QV4::CompiledData::Property::DateTime: - *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); - break; - case QV4::CompiledData::Property::Rect: - *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); - break; - case QV4::CompiledData::Property::Size: - *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); - break; - case QV4::CompiledData::Property::Point: - *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); - break; - case QV4::CompiledData::Property::Custom: - *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); - break; - case QV4::CompiledData::Property::Variant: + case QV4::CompiledData::BuiltinType::Size: + *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); + break; + case QV4::CompiledData::BuiltinType::Point: + *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); + break; + case QV4::CompiledData::BuiltinType::Variant: + *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); + break; + case QV4::CompiledData::BuiltinType::Font: + case QV4::CompiledData::BuiltinType::Time: + case QV4::CompiledData::BuiltinType::Color: + case QV4::CompiledData::BuiltinType::Vector2D: + case QV4::CompiledData::BuiltinType::Vector3D: + case QV4::CompiledData::BuiltinType::Vector4D: + case QV4::CompiledData::BuiltinType::Matrix4x4: + case QV4::CompiledData::BuiltinType::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + QVariant propertyAsVariant; + if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) + propertyAsVariant = v->d()->data(); + QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); + } + break; + case QV4::CompiledData::BuiltinType::Var: + if (ep) { *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); - break; - case QV4::CompiledData::Property::CustomList: { + } else { + // if the context was disposed, we just return an invalid variant from read. + *reinterpret_cast<QVariant *>(a[0]) = QVariant(); + } + break; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: + if (property.isList) { QList<QObject *> *list = readPropertyAsList(id); QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]); *p = QQmlListProperty<QObject>(object, list, - list_append, list_count, list_at, - list_clear); + list_append, list_count, list_at, + list_clear); p->dummy1 = this; p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id)); - break; + } else { + *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); } - case QV4::CompiledData::Property::Font: - case QV4::CompiledData::Property::Time: - case QV4::CompiledData::Property::Color: - case QV4::CompiledData::Property::Vector2D: - case QV4::CompiledData::Property::Vector3D: - case QV4::CompiledData::Property::Vector4D: - case QV4::CompiledData::Property::Matrix4x4: - case QV4::CompiledData::Property::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - QVariant propertyAsVariant; - if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) - propertyAsVariant = v->d()->data(); - QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); + } + + } else if (c == QMetaObject::WriteProperty) { + bool needActivate = false; + switch (t) { + case QV4::CompiledData::BuiltinType::Int: + needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); + writeProperty(id, *reinterpret_cast<int *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Bool: + needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); + writeProperty(id, *reinterpret_cast<bool *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Real: + needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); + writeProperty(id, *reinterpret_cast<double *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::String: + needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); + writeProperty(id, *reinterpret_cast<QString *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Url: + needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); + writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Date: + needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); + writeProperty(id, *reinterpret_cast<QDate *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::DateTime: + needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); + writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Rect: + needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); + writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Size: + needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); + writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Point: + needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); + writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Variant: + writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Font: + case QV4::CompiledData::BuiltinType::Time: + case QV4::CompiledData::BuiltinType::Color: + case QV4::CompiledData::BuiltinType::Vector2D: + case QV4::CompiledData::BuiltinType::Vector3D: + case QV4::CompiledData::BuiltinType::Vector4D: + case QV4::CompiledData::BuiltinType::Matrix4x4: + case QV4::CompiledData::BuiltinType::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + if (!v) { + md->set(engine, id, engine->newVariantObject(QVariant())); + v = (md->data() + id)->as<QV4::VariantObject>(); + QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); } - break; - case QV4::CompiledData::Property::Var: - Q_UNREACHABLE(); + needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data()); + QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data()); } - - } else if (c == QMetaObject::WriteProperty) { - - switch(t) { - case QV4::CompiledData::Property::Int: - needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); - writeProperty(id, *reinterpret_cast<int *>(a[0])); - break; - case QV4::CompiledData::Property::Bool: - needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); - writeProperty(id, *reinterpret_cast<bool *>(a[0])); - break; - case QV4::CompiledData::Property::Real: - needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); - writeProperty(id, *reinterpret_cast<double *>(a[0])); - break; - case QV4::CompiledData::Property::String: - needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); - writeProperty(id, *reinterpret_cast<QString *>(a[0])); - break; - case QV4::CompiledData::Property::Url: - needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); - writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); - break; - case QV4::CompiledData::Property::Date: - needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); - writeProperty(id, *reinterpret_cast<QDate *>(a[0])); - break; - case QV4::CompiledData::Property::DateTime: - needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); - writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); - break; - case QV4::CompiledData::Property::Rect: - needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); - writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); - break; - case QV4::CompiledData::Property::Size: - needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); - writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); - break; - case QV4::CompiledData::Property::Point: - needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); - writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); - break; - case QV4::CompiledData::Property::Custom: - needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); - writeProperty(id, *reinterpret_cast<QObject **>(a[0])); - break; - case QV4::CompiledData::Property::Variant: + break; + case QV4::CompiledData::BuiltinType::Var: + if (ep) writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - break; - case QV4::CompiledData::Property::CustomList: + break; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: + if (property.isList) { // Writing such a property is not supported. Content is added through the list property // methods. - break; - case QV4::CompiledData::Property::Font: - case QV4::CompiledData::Property::Time: - case QV4::CompiledData::Property::Color: - case QV4::CompiledData::Property::Vector2D: - case QV4::CompiledData::Property::Vector3D: - case QV4::CompiledData::Property::Vector4D: - case QV4::CompiledData::Property::Matrix4x4: - case QV4::CompiledData::Property::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); - if (!v) { - md->set(engine, id, engine->newVariantObject(QVariant())); - v = (md->data() + id)->as<QV4::VariantObject>(); - QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); - } - needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data()); - QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data()); - } - break; - case QV4::CompiledData::Property::Var: - Q_UNREACHABLE(); + } else { + needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); + writeProperty(id, *reinterpret_cast<QObject **>(a[0])); } - } - } + } - if (c == QMetaObject::WriteProperty && needActivate) { - activate(object, methodOffset() + id, nullptr); + if (needActivate) + activate(object, methodOffset() + id, nullptr); } return -1; @@ -891,17 +869,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (!targetDData->propertyCache) return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); - // Value type property + // Value type property or deep alias QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); - Q_ASSERT(valueType); + if (valueType) { - valueType->read(target, coreIndex); - int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); + valueType->read(target, coreIndex); + int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); - if (c == QMetaObject::WriteProperty) - valueType->write(target, coreIndex, nullptr); + if (c == QMetaObject::WriteProperty) + valueType->write(target, coreIndex, nullptr); - return rv; + return rv; + } else { + // deep alias + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + return QMetaObject::metacall(target, c, valueTypePropertyIndex, a); + } } else { return QMetaObject::metacall(target, c, coreIndex, a); @@ -951,21 +935,38 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; // The dynamic method with that id is not available. } - const unsigned int parameterCount = function->formalParameterCount(); + auto methodData = cache->method(_id); + auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr; + + const unsigned int parameterCount = (arguments && arguments->names) ? arguments->names->count() : 0; + Q_ASSERT(parameterCount == function->formalParameterCount()); + QV4::JSCallData jsCallData(scope, parameterCount); *jsCallData->thisObject = v4->global(); - for (uint ii = 0; ii < parameterCount; ++ii) - jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); + for (uint ii = 0; ii < parameterCount; ++ii) { + jsCallData->args[ii] = scope.engine->metaTypeToJS(arguments->arguments[ii + 1], a[ii + 1]); + } + const int returnType = methodData->propType(); QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); - if (a[0]) *(QVariant *)a[0] = QVariant(); + if (a[0]) { + QMetaType::destruct(returnType, a[0]); + QMetaType::construct(returnType, a[0], nullptr); + } } else { - if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + if (a[0]) { + // When the return type is QVariant, JS objects are to be returned as QJSValue wrapped in + // QVariant. + if (returnType == QMetaType::QVariant) + *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + else + scope.engine->metaTypeFromJS(result, returnType, a[0]); + } } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. @@ -1000,7 +1001,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) @@ -1025,7 +1026,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) @@ -1065,7 +1066,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) { - if (compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var) { + if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index dbcc9d2884..5025987586 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -65,9 +65,7 @@ #include "qqmlguard_p.h" #include "qqmlcontext_p.h" -#include "qqmlpropertycache_p.h" -#include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> #include <private/qv4object_p.h> @@ -146,7 +144,7 @@ class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId); + QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId); ~QQmlVMEMetaObject() override; bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; @@ -228,7 +226,7 @@ public: // keep a reference to the compilation unit in order to still // do property access when the context has been invalidated. - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QV4::CompiledData::Object *compiledObject; }; diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 9877cc027f..4db0562c0e 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -39,8 +39,6 @@ #include "qqmlxmlhttprequest_p.h" -#include <private/qv8engine_p.h> - #include "qqmlengine.h" #include "qqmlengine_p.h" #include <private/qqmlrefcount_p.h> @@ -70,8 +68,6 @@ using namespace QV4; -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) - #define V4THROW_REFERENCE(string) \ do { \ ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \ @@ -99,7 +95,7 @@ struct QQmlXMLHttpRequestData { static inline QQmlXMLHttpRequestData *xhrdata(ExecutionEngine *v4) { - return (QQmlXMLHttpRequestData *)v4->v8Engine->xmlHttpRequestData(); + return (QQmlXMLHttpRequestData *)v4->xmlHttpRequestData(); } QQmlXMLHttpRequestData::QQmlXMLHttpRequestData() @@ -595,7 +591,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) if (d->nodePrototype.isUndefined()) { ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>()); d->nodePrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->nodePrototype.value(); } @@ -644,7 +640,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine) p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr); d->elementPrototype.set(engine, p); - engine->v8Engine->freezeObject(p); + engine->freezeObject(p); } return d->elementPrototype.value(); } @@ -661,7 +657,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr); p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr); d->attrPrototype.set(engine, p); - engine->v8Engine->freezeObject(p); + engine->freezeObject(p); } return d->attrPrototype.value(); } @@ -717,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr); p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr); d->characterDataPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->characterDataPrototype.value(); } @@ -753,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr); p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr); d->textPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->textPrototype.value(); } @@ -768,7 +764,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4) ScopedObject pp(scope); p->setPrototypeUnchecked((pp = Text::prototype(v4))); d->cdataPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->cdataPrototype.value(); } @@ -786,7 +782,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr); p->defineAccessorProperty(QStringLiteral("documentElement"), method_documentElement, nullptr); d->documentPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->documentPrototype.value(); } @@ -1651,7 +1647,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->qmlEngine()->networkAccessManager(), scope.engine); Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototypeUnchecked(proto); @@ -2073,6 +2069,4 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) QT_END_NAMESPACE -#endif // xmlstreamreader && qml_network - #include <qqmlxmlhttprequest.moc> diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 4422195e3d..d634a48443 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -40,6 +40,7 @@ #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> @@ -47,7 +48,6 @@ #if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> #endif -#include <private/qv8engine_p.h> #include <private/qqmldelayedcallqueue_p.h> #include <QFileInfo> @@ -439,7 +439,7 @@ ReturnedValue QtObject::method_font(const FunctionObject *b, const Value *, cons QV4::ExecutionEngine *v4 = scope.engine; bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(argv[0]), v4, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, argv[0], v4, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified"); return scope.engine->fromVariant(v); @@ -559,7 +559,7 @@ ReturnedValue QtObject::method_matrix4x4(const FunctionObject *b, const Value *, if (argc == 1 && argv[0].isObject()) { bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(argv[0]), scope.engine, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, argv[0], scope.engine, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array"); return scope.engine->fromVariant(v); @@ -1710,10 +1710,8 @@ ReturnedValue ConsoleObject::method_time(const FunctionObject *b, const Value *, if (argc != 1) THROW_GENERIC_ERROR("console.time(): Invalid arguments"); - QV8Engine *v8engine = scope.engine->v8Engine; - QString name = argv[0].toQStringNoThrow(); - v8engine->startTimer(name); + scope.engine->startTimer(name); return QV4::Encode::undefined(); } @@ -1723,11 +1721,9 @@ ReturnedValue ConsoleObject::method_timeEnd(const FunctionObject *b, const Value if (argc != 1) THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments"); - QV8Engine *v8engine = scope.engine->v8Engine; - QString name = argv[0].toQStringNoThrow(); bool wasRunning; - qint64 elapsed = v8engine->stopTimer(name, &wasRunning); + qint64 elapsed = scope.engine->stopTimer(name, &wasRunning); if (wasRunning) { qDebug("%s: %llims", qPrintable(name), elapsed); } @@ -1743,13 +1739,12 @@ ReturnedValue ConsoleObject::method_count(const FunctionObject *b, const Value * Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; - QV8Engine *v8engine = scope.engine->v8Engine; QV4::CppStackFrame *frame = v4->currentStackFrame; QString scriptName = frame->source(); - int value = v8engine->consoleCountHelper(scriptName, frame->lineNumber(), 0); + int value = v4->consoleCountHelper(scriptName, frame->lineNumber(), 0); QString message = name + QLatin1String(": ") + QString::number(value); QMessageLogger(qPrintable(scriptName), frame->lineNumber(), @@ -1972,29 +1967,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); QString context; - if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { - QString path = ctxt->urlString(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - int lastDot = path.lastIndexOf(QLatin1Char('.')); - int length = lastDot - (lastSlash + 1); - context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); - } else { - CppStackFrame *frame = scope.engine->currentStackFrame; - // The first non-empty source URL in the call stack determines the translation context. - while (frame && context.isEmpty()) { - if (CompiledData::CompilationUnit *unit = frame->v4Function->compilationUnit) { - QString fileName = unit->fileName(); - QUrl url(unit->fileName()); - if (url.isValid() && url.isRelative()) { - context = url.fileName(); - } else { - context = QQmlFile::urlToLocalFileOrQrc(fileName); - if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) - context = fileName; - } - context = QFileInfo(context).baseName(); + CppStackFrame *frame = scope.engine->currentStackFrame; + // 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); + QString fileName = unit->fileName(); + QUrl url(unit->fileName()); + if (url.isValid() && url.isRelative()) { + context = url.fileName(); + } else { + context = QQmlFile::urlToLocalFileOrQrc(fileName); + if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) + context = fileName; } - frame = frame->parent; + context = QFileInfo(context).baseName(); + } + frame = frame->parent; + } + + if (context.isEmpty()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { + QString path = ctxt->urlString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + int lastDot = path.lastIndexOf(QLatin1Char('.')); + int length = lastDot - (lastSlash + 1); + context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } } @@ -2173,8 +2171,7 @@ function. */ ReturnedValue QtObject::method_callLater(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QV8Engine *v8engine = b->engine()->v8Engine; - return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc); + return b->engine()->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp deleted file mode 100644 index d76344b613..0000000000 --- a/src/qml/qml/v8/qv8engine.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv8engine_p.h" - -#if QT_CONFIG(qml_sequence_object) -#include "qv4sequenceobject_p.h" -#endif - -#include "private/qjsengine_p.h" - -#include <private/qqmlbuiltinfunctions_p.h> -#include <private/qqmllist_p.h> -#include <private/qqmlengine_p.h> -#if QT_CONFIG(qml_xml_http_request) -#include <private/qqmlxmlhttprequest_p.h> -#endif -#if QT_CONFIG(qml_locale) -#include <private/qqmllocale_p.h> -#endif -#include <private/qqmlglobal_p.h> -#include <private/qqmlmemoryprofiler_p.h> -#include <private/qqmlplatform_p.h> -#include <private/qjsvalue_p.h> -#include <private/qqmltypewrapper_p.h> -#include <private/qqmlvaluetypewrapper_p.h> -#include <private/qqmllistwrapper_p.h> -#include <private/qv4scopedvalue_p.h> - -#include "qv4domerrors_p.h" -#include "qv4sqlerrors_p.h" - -#include <QtCore/qjsonarray.h> -#include <QtCore/qjsonobject.h> -#include <QtCore/qjsonvalue.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qdatastream.h> -#include <private/qsimd_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4objectiterator_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <private/qv4mm_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4globalobject_p.h> -#include <private/qv4regexpobject_p.h> -#include <private/qv4variantobject_p.h> -#include <private/qv4script_p.h> -#include <private/qv4include_p.h> -#include <private/qv4jsonobject_p.h> -#include <private/qv4identifiertable_p.h> - -Q_DECLARE_METATYPE(QList<int>) - - -// XXX TODO: Need to check all the global functions will also work in a worker script where the -// QQmlEngine is not available -QT_BEGIN_NAMESPACE - -template <typename ReturnType> -ReturnType convertJSValueToVariantType(const QJSValue &value) -{ - return value.toVariant().value<ReturnType>(); -} - -static void saveJSValue(QDataStream &stream, const void *data) -{ - const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data); - quint32 isNullOrUndefined = 0; - if (jsv->isNull()) - isNullOrUndefined |= 0x1; - if (jsv->isUndefined()) - isNullOrUndefined |= 0x2; - stream << isNullOrUndefined; - if (!isNullOrUndefined) - reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream); -} - -static void restoreJSValue(QDataStream &stream, void *data) -{ - QJSValue *jsv = reinterpret_cast<QJSValue*>(data); - - quint32 isNullOrUndefined; - stream >> isNullOrUndefined; - - if (isNullOrUndefined & 0x1) { - *jsv = QJSValue(QJSValue::NullValue); - } else if (isNullOrUndefined & 0x2) { - *jsv = QJSValue(); - } else { - QVariant v; - v.load(stream); - QJSValuePrivate::setVariant(jsv, v); - } -} - -QV8Engine::QV8Engine(QV4::ExecutionEngine *v4) - : m_engine(nullptr) - , m_v4Engine(v4) -#if QT_CONFIG(qml_xml_http_request) - , m_xmlHttpRequestData(nullptr) -#endif -{ -#ifndef Q_OS_WASM // wasm does not have working simd QTBUG-63924 -#ifdef Q_PROCESSOR_X86_32 - if (!qCpuHasFeature(SSE2)) { - qFatal("This program requires an X86 processor that supports SSE2 extension, at least a Pentium 4 or newer"); - } -#endif -#endif - - QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine"); - qMetaTypeId<QJSValue>(); - qMetaTypeId<QList<int> >(); - - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) - QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) - QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) - QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); - QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); - - m_delayedCallQueue.init(m_v4Engine); - - QV4::QObjectWrapper::initializeBindings(m_v4Engine); -} - -QV8Engine::~QV8Engine() -{ - qDeleteAll(m_extensionData); - m_extensionData.clear(); - -#if QT_CONFIG(qml_xml_http_request) - qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); - m_xmlHttpRequestData = nullptr; -#endif -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QV8Engine::networkAccessManager() -{ - return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); -} -#endif - -const QSet<QString> &QV8Engine::illegalNames() const -{ - return m_illegalNames; -} - -void QV8Engine::initializeGlobal() -{ - QV4::Scope scope(m_v4Engine); - QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions); - - QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine)); - m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); - -#if QT_CONFIG(qml_locale) - QQmlLocale::registerStringLocaleCompare(m_v4Engine); - QQmlDateExtension::registerExtension(m_v4Engine); - QQmlNumberExtension::registerExtension(m_v4Engine); -#endif - -#if QT_CONFIG(qml_xml_http_request) - qt_add_domexceptions(m_v4Engine); - m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); -#endif - - qt_add_sqlexceptions(m_v4Engine); - - { - for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) { - if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) { - QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i); - m_illegalNames.insert(id.toQString()); - } - } - } -} - -static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) -{ - if (object->as<QV4::QObjectWrapper>()) - return; - - QV4::Scope scope(v4); - - bool instanceOfObject = false; - QV4::ScopedObject p(scope, object->getPrototypeOf()); - while (p) { - if (p->d() == v4->objectPrototype()->d()) { - instanceOfObject = true; - break; - } - p = p->getPrototypeOf(); - } - if (!instanceOfObject) - return; - - QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen(); - if (object->internalClass() == frozen) - return; - object->setInternalClass(frozen); - - QV4::ScopedObject o(scope); - for (uint i = 0; i < frozen->size; ++i) { - if (!frozen->nameMap.at(i).isStringOrSymbol()) - continue; - o = *object->propertyData(i); - if (o) - freeze_recursive(v4, o); - } -} - -void QV8Engine::freezeObject(const QV4::Value &value) -{ - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject o(scope, value); - freeze_recursive(m_v4Engine, o); -} - -struct QV8EngineRegistrationData -{ - QV8EngineRegistrationData() : extensionCount(0) {} - - QMutex mutex; - int extensionCount; -}; -Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData); - -QMutex *QV8Engine::registrationMutex() -{ - return ®istrationData()->mutex; -} - -int QV8Engine::registerExtension() -{ - return registrationData()->extensionCount++; -} - -void QV8Engine::setExtensionData(int index, Deletable *data) -{ - if (m_extensionData.count() <= index) - m_extensionData.resize(index + 1); - - if (m_extensionData.at(index)) - delete m_extensionData.at(index); - - m_extensionData[index] = data; -} - -void QV8Engine::initQmlGlobalObject() -{ - initializeGlobal(); - freezeObject(*m_v4Engine->globalObject); -} - -void QV8Engine::setEngine(QQmlEngine *engine) -{ - m_engine = engine; - initQmlGlobalObject(); -} - -void QV8Engine::startTimer(const QString &timerName) -{ - if (!m_time.isValid()) - m_time.start(); - m_startedTimers[timerName] = m_time.elapsed(); -} - -qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning) -{ - if (!m_startedTimers.contains(timerName)) { - *wasRunning = false; - return 0; - } - *wasRunning = true; - qint64 startedAt = m_startedTimers.take(timerName); - return m_time.elapsed() - startedAt; -} - -int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column) -{ - const QString key = file + QString::number(line) + QString::number(column); - int number = m_consoleCount.value(key, 0); - number++; - m_consoleCount.insert(key, number); - return number; -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h deleted file mode 100644 index 23559618ef..0000000000 --- a/src/qml/qml/v8/qv8engine_p.h +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLV8ENGINE_P_H -#define QQMLV8ENGINE_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/qglobal.h> -#include <QtCore/qvariant.h> -#include <QtCore/qset.h> -#include <QtCore/qmutex.h> -#include <QtCore/qstack.h> -#include <QtCore/qstringlist.h> -#include <QtCore/QElapsedTimer> -#include <QtCore/QThreadStorage> - -#include <QtQml/qjsengine.h> -#include "private/qintrusivelist_p.h" - - -#include <private/qv4value_p.h> -#include <private/qv4identifier_p.h> -#include <private/qv4context_p.h> -#include <private/qv4stackframe_p.h> -#include <private/qqmldelayedcallqueue_p.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - struct ArrayObject; - struct ExecutionEngine; - struct QObjectMethod; -} - -#define V4_DEFINE_EXTENSION(dataclass, datafunction) \ - static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \ - { \ - static int extensionId = -1; \ - if (extensionId == -1) { \ - QV8Engine::registrationMutex()->lock(); \ - if (extensionId == -1) \ - extensionId = QV8Engine::registerExtension(); \ - QV8Engine::registrationMutex()->unlock(); \ - } \ - dataclass *rv = (dataclass *)engine->v8Engine->extensionData(extensionId); \ - if (!rv) { \ - rv = new dataclass(engine); \ - engine->v8Engine->setExtensionData(extensionId, rv); \ - } \ - return rv; \ - } \ - -// Used to allow a QObject method take and return raw V4 handles without having to expose -// 48 in the public API. -// Use like this: -// class MyClass : public QObject { -// Q_OBJECT -// ... -// Q_INVOKABLE void myMethod(QQmlV4Function*); -// }; -// The QQmlV8Function - and consequently the arguments and return value - only remains -// valid during the call. If the return value isn't set within myMethod(), the will return -// undefined. - -class QQmlV4Function -{ -public: - int length() const { return callData->argc(); } - QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } - void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } - QV4::ExecutionEngine *v4engine() const { return e; } -private: - friend struct QV4::QObjectMethod; - QQmlV4Function(); - QQmlV4Function(const QQmlV4Function &); - QQmlV4Function &operator=(const QQmlV4Function &); - - QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e) - : callData(callData), retVal(retVal), e(e) - { - callData->thisObject = QV4::Encode::undefined(); - } - - QV4::CallData *callData; - QV4::Value *retVal; - QV4::ExecutionEngine *e; -}; - -class Q_QML_PRIVATE_EXPORT QQmlV4Handle -{ -public: - QQmlV4Handle() : d(QV4::Encode::undefined()) {} - explicit QQmlV4Handle(const QV4::Value &v) : d(v.asReturnedValue()) {} - explicit QQmlV4Handle(QV4::ReturnedValue v) : d(v) {} - - operator QV4::ReturnedValue() const { return d; } - -private: - quint64 d; -}; - -class QObject; -class QQmlEngine; -class QNetworkAccessManager; -class QQmlContextData; - -class Q_QML_PRIVATE_EXPORT QV8Engine -{ - friend class QJSEngine; -public: -// static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } - static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; } - - QV8Engine(QV4::ExecutionEngine *v4); - virtual ~QV8Engine(); - - // This enum should be in sync with QQmlEngine::ObjectOwnership - enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; - - struct Deletable { - virtual ~Deletable() {} - }; - - void initQmlGlobalObject(); - void setEngine(QQmlEngine *engine); - QQmlEngine *engine() { return m_engine; } - QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } - -#if QT_CONFIG(qml_xml_http_request) - void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } -#endif - - void freezeObject(const QV4::Value &value); - -#if QT_CONFIG(qml_network) - // Return the network access manager for this engine. By default this returns the network - // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 - // instance (like in WorkerScript). - virtual QNetworkAccessManager *networkAccessManager(); -#endif - - // Return the list of illegal id names (the names of the properties on the global object) - const QSet<QString> &illegalNames() const; - - static QMutex *registrationMutex(); - static int registerExtension(); - - inline Deletable *extensionData(int) const; - void setExtensionData(int, Deletable *); - -public: - // used for console.time(), console.timeEnd() - void startTimer(const QString &timerName); - qint64 stopTimer(const QString &timerName, bool *wasRunning); - - // used for console.count() - int consoleCountHelper(const QString &file, quint16 line, quint16 column); - -protected: - QQmlEngine *m_engine; - QQmlDelayedCallQueue m_delayedCallQueue; - - QV4::ExecutionEngine *m_v4Engine; - -#if QT_CONFIG(qml_xml_http_request) - void *m_xmlHttpRequestData; -#endif - - QVector<Deletable *> m_extensionData; - - QSet<QString> m_illegalNames; - - QElapsedTimer m_time; - QHash<QString, qint64> m_startedTimers; - - QHash<QString, quint32> m_consoleCount; - - void initializeGlobal(); - -private: - Q_DISABLE_COPY(QV8Engine) -}; - -inline QV8Engine::Deletable *QV8Engine::extensionData(int index) const -{ - if (index < m_extensionData.count()) - return m_extensionData[index]; - else - return nullptr; -} - - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlV4Handle) - -#endif // QQMLV8ENGINE_P_H diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri index 4592022939..fbcc47de0c 100644 --- a/src/qml/qml/v8/v8.pri +++ b/src/qml/qml/v8/v8.pri @@ -1,11 +1,9 @@ HEADERS += \ - $$PWD/qv8engine_p.h \ $$PWD/qv4domerrors_p.h \ $$PWD/qv4sqlerrors_p.h \ $$PWD/qqmlbuiltinfunctions_p.h SOURCES += \ - $$PWD/qv8engine.cpp \ $$PWD/qv4domerrors.cpp \ $$PWD/qv4sqlerrors.cpp \ $$PWD/qqmlbuiltinfunctions.cpp |