diff options
Diffstat (limited to 'src/qml/qml')
233 files changed, 90055 insertions, 0 deletions
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri new file mode 100644 index 0000000000..f2fec4e2dd --- /dev/null +++ b/src/qml/qml/ftw/ftw.pri @@ -0,0 +1,29 @@ +HEADERS += \ + $$PWD/qbitfield_p.h \ + $$PWD/qintrusivelist_p.h \ + $$PWD/qpodvector_p.h \ + $$PWD/qhashedstring_p.h \ + $$PWD/qqmlrefcount_p.h \ + $$PWD/qqmlpool_p.h \ + $$PWD/qfieldlist_p.h \ + $$PWD/qfastmetabuilder_p.h \ + $$PWD/qhashfield_p.h \ + $$PWD/qqmlthread_p.h \ + $$PWD/qfinitestack_p.h \ + $$PWD/qrecursionwatcher_p.h \ + $$PWD/qdeletewatcher_p.h \ + $$PWD/qrecyclepool_p.h \ + $$PWD/qflagpointer_p.h \ + $$PWD/qqmltrace_p.h \ + $$PWD/qpointervaluepair_p.h \ + $$PWD/qlazilyallocated_p.h \ + +SOURCES += \ + $$PWD/qintrusivelist.cpp \ + $$PWD/qhashedstring.cpp \ + $$PWD/qqmlpool.cpp \ + $$PWD/qfastmetabuilder.cpp \ + $$PWD/qqmlthread.cpp \ + $$PWD/qqmltrace.cpp \ + +contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) diff --git a/src/qml/qml/ftw/qbitfield_p.h b/src/qml/qml/ftw/qbitfield_p.h new file mode 100644 index 0000000000..75f80dd896 --- /dev/null +++ b/src/qml/qml/ftw/qbitfield_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBITFIELD_P_H +#define QBITFIELD_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> + +QT_BEGIN_NAMESPACE + +class QBitField +{ +public: + inline QBitField(); + inline QBitField(const quint32 *, int bits); + inline QBitField(const QBitField &); + inline ~QBitField(); + + inline QBitField &operator=(const QBitField &); + + inline quint32 size() const; + inline QBitField united(const QBitField &); + inline bool testBit(int) const; + +private: + quint32 bits:31; + quint32 *ownData; + const quint32 *data; +}; + +QBitField::QBitField() +: bits(0), ownData(0), data(0) +{ +} + +QBitField::QBitField(const quint32 *bitData, int bitCount) +: bits((quint32)bitCount), ownData(0), data(bitData) +{ +} + +QBitField::QBitField(const QBitField &other) +: bits(other.bits), ownData(other.ownData), data(other.data) +{ + if (ownData) + ++(*ownData); +} + +QBitField::~QBitField() +{ + if (ownData) + if(0 == --(*ownData)) delete [] ownData; +} + +QBitField &QBitField::operator=(const QBitField &other) +{ + if (other.data == data) + return *this; + + if (ownData) + if(0 == --(*ownData)) delete [] ownData; + + bits = other.bits; + ownData = other.ownData; + data = other.data; + + if (ownData) + ++(*ownData); + + return *this; +} + +inline quint32 QBitField::size() const +{ + return bits; +} + +QBitField QBitField::united(const QBitField &o) +{ + if (o.bits == 0) { + return *this; + } else if (bits == 0) { + return o; + } else { + int max = (bits > o.bits)?bits:o.bits; + int length = (max + 31) / 32; + QBitField rv; + rv.bits = max; + rv.ownData = new quint32[length + 1]; + *(rv.ownData) = 1; + rv.data = rv.ownData + 1; + if (bits > o.bits) { + ::memcpy((quint32 *)rv.data, data, length * sizeof(quint32)); + for (quint32 ii = 0; ii < (o.bits + quint32(31)) / 32; ++ii) + ((quint32 *)rv.data)[ii] |= o.data[ii]; + } else { + ::memcpy((quint32 *)rv.data, o.data, length * sizeof(quint32)); + for (quint32 ii = 0; ii < (bits + quint32(31)) / 32; ++ii) + ((quint32 *)rv.data)[ii] |= data[ii]; + } + return rv; + } +} + +bool QBitField::testBit(int b) const +{ + Q_ASSERT(b >= 0); + if ((quint32)b < bits) { + return data[b / 32] & (1 << (b % 32)); + } else { + return false; + } +} + +QT_END_NAMESPACE + +#endif // QBITFIELD_P_H diff --git a/src/qml/qml/ftw/qdeletewatcher_p.h b/src/qml/qml/ftw/qdeletewatcher_p.h new file mode 100644 index 0000000000..9f7b100429 --- /dev/null +++ b/src/qml/qml/ftw/qdeletewatcher_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDELETEWATCHER_P_H +#define QDELETEWATCHER_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> + +QT_BEGIN_NAMESPACE + +class QDeleteWatchable +{ +public: + inline QDeleteWatchable(); + inline ~QDeleteWatchable(); +private: + friend class QDeleteWatcher; + bool *_w; +}; + +class QDeleteWatcher { +public: + inline QDeleteWatcher(QDeleteWatchable *data); + inline ~QDeleteWatcher(); + inline bool wasDeleted() const; +private: + void *operator new(size_t); + bool *_w; + bool _s; + QDeleteWatchable *m_d; +}; + +QDeleteWatchable::QDeleteWatchable() +: _w(0) +{ +} + +QDeleteWatchable::~QDeleteWatchable() +{ + if (_w) *_w = true; +} + +QDeleteWatcher::QDeleteWatcher(QDeleteWatchable *data) +: _s(false), m_d(data) +{ + if (!m_d->_w) + m_d->_w = &_s; + _w = m_d->_w; +} + +QDeleteWatcher::~QDeleteWatcher() +{ + if (false == *_w && &_s == m_d->_w) + m_d->_w = 0; +} + +bool QDeleteWatcher::wasDeleted() const +{ + return *_w; +} + +QT_END_NAMESPACE + +#endif // QDELETEWATCHER_P_H diff --git a/src/qml/qml/ftw/qfastmetabuilder.cpp b/src/qml/qml/ftw/qfastmetabuilder.cpp new file mode 100644 index 0000000000..9663c1e944 --- /dev/null +++ b/src/qml/qml/ftw/qfastmetabuilder.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfastmetabuilder_p.h" + +#include <QtCore/qmetaobject.h> + +QT_BEGIN_NAMESPACE + +struct QFastMetaBuilderHeader +{ + int fieldCount; +}; + +struct QMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + int constructorCount, constructorData; //since revision 2 + int flags; //since revision 3 + int signalCount; //since revision 4 +}; + +enum MetaObjectFlag { + DynamicMetaObject = 0x01 +}; + +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000, + Revisioned = 0x00800000 +}; + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40, + MethodRevisioned = 0x80 +}; + +#define FMBHEADER_FIELD_COUNT 1 + +#define HEADER_FIELD_COUNT 14 +#define CLASSINFO_FIELD_COUNT 2 +#define METHOD_FIELD_COUNT 5 +#define PROPERTY_FIELD_COUNT 3 +#define PROPERTY_NOTIFY_FIELD_COUNT 1 + +static inline uint *fieldPointer(QByteArray &data) +{ return reinterpret_cast<uint *>(data.data()) + FMBHEADER_FIELD_COUNT; } + +static inline const uint *fieldPointer(const QByteArray &data) +{ return reinterpret_cast<const uint *>(data.constData()) + FMBHEADER_FIELD_COUNT; } + +static inline QMetaObjectPrivate *priv(QByteArray &data) +{ return reinterpret_cast<QMetaObjectPrivate*>(fieldPointer(data)); } + +static inline const QMetaObjectPrivate *priv(const QByteArray &data) +{ return reinterpret_cast<const QMetaObjectPrivate*>(fieldPointer(data)); } + +static inline QFastMetaBuilderHeader *header(QByteArray &data) +{ return reinterpret_cast<QFastMetaBuilderHeader*>(data.data()); } + +static inline const QFastMetaBuilderHeader *header(const QByteArray &data) +{ return reinterpret_cast<const QFastMetaBuilderHeader*>(data.constData()); } + +QFastMetaBuilder::QFastMetaBuilder() +: m_zeroPtr(0), m_stringData(0), m_stringDataLength(0), m_stringDataAllocated(0) +{ +} + +QFastMetaBuilder::~QFastMetaBuilder() +{ +} + +QFastMetaBuilder::StringRef QFastMetaBuilder::init(int classNameLength, + int propertyCount, int methodCount, + int signalCount, int classInfoCount) +{ + Q_ASSERT(m_data.isEmpty()); + Q_ASSERT(classNameLength > 0); + Q_ASSERT(propertyCount >= 0); + Q_ASSERT(methodCount >= 0); + Q_ASSERT(signalCount >= 0); + Q_ASSERT(classInfoCount >= 0); + + int fieldCount = FMBHEADER_FIELD_COUNT + + HEADER_FIELD_COUNT + + propertyCount * (PROPERTY_FIELD_COUNT + PROPERTY_NOTIFY_FIELD_COUNT) + + methodCount * (METHOD_FIELD_COUNT) + + signalCount * (METHOD_FIELD_COUNT) + + classInfoCount * CLASSINFO_FIELD_COUNT; + + m_data.resize(fieldCount * sizeof(uint) + classNameLength + 1); + m_stringData = m_data.data() + m_data.size() - classNameLength - 1; + m_stringDataLength = classNameLength + 1; + m_stringDataAllocated = classNameLength + 1; + m_stringData[classNameLength] = 0; + m_zeroPtr = classNameLength; + + header(m_data)->fieldCount = fieldCount; + + QMetaObjectPrivate *p = priv(m_data); + + int dataIndex = HEADER_FIELD_COUNT; + + p->revision = 4; + p->className = 0; + + // Class infos + p->classInfoCount = classInfoCount; + if (p->classInfoCount) { + p->classInfoData = dataIndex; + dataIndex += p->classInfoCount * CLASSINFO_FIELD_COUNT; + } else { + p->classInfoData = 0; + } + + // Methods + p->methodCount = methodCount + signalCount; + if (p->methodCount) { + p->methodData = dataIndex; + dataIndex += p->methodCount * METHOD_FIELD_COUNT; + } else { + p->methodData = 0; + } + p->signalCount = signalCount; + + // Properties + p->propertyCount = propertyCount; + if (p->propertyCount) { + p->propertyData = dataIndex; + dataIndex += p->propertyCount * (PROPERTY_FIELD_COUNT + PROPERTY_NOTIFY_FIELD_COUNT); + } else { + p->propertyData = 0; + } + + // Flags + p->flags = DynamicMetaObject; // Always dynamic + + // Enums and constructors not supported + p->enumeratorCount = 0; + p->enumeratorData = 0; + p->constructorCount = 0; + p->constructorData = 0; + + StringRef className; + className._b = this; + className._o = 0; + className._l = classNameLength; + return className; +} + +// Allocate a string of \a length. \a length should *not* include the null terminator. +QFastMetaBuilder::StringRef QFastMetaBuilder::newString(int length) +{ + Q_ASSERT(length > 0); + + StringRef sr; + sr._b = this; + sr._o = m_stringDataLength; + sr._l = length; + + m_stringDataLength += length + 1 /* for null terminator */; + + return sr; +} + +void QFastMetaBuilder::setClassInfo(int index, const StringRef &key, const StringRef &value) +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(!key.isEmpty() && !value.isEmpty()); + + QMetaObjectPrivate *p = priv(m_data); + Q_ASSERT(index < p->classInfoCount); + + uint *ptr = fieldPointer(m_data) + p->classInfoData + index * CLASSINFO_FIELD_COUNT; + // classinfo: key, value + ptr[0] = key.offset(); ptr[1] = value.offset(); +} + +void QFastMetaBuilder::setProperty(int index, const StringRef &name, const StringRef &type, + QMetaType::Type mtype, PropertyFlag flags, int notifySignal) +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(!name.isEmpty() && !type.isEmpty()); + + QMetaObjectPrivate *p = priv(m_data); + Q_ASSERT(index < p->propertyCount); + + uint *ptr = fieldPointer(m_data) + p->propertyData + index * PROPERTY_FIELD_COUNT; + // properties: name, type, flags + ptr[0] = name.offset(); + ptr[1] = type.offset(); + if (notifySignal == -1) { + ptr[2] = mtype << 24; + ptr[2] |= flags | Scriptable | Readable; + *(fieldPointer(m_data) + p->propertyData + p->propertyCount * PROPERTY_FIELD_COUNT + index) = 0; + } else { + ptr[2] = mtype << 24; + ptr[2] |= flags | Scriptable | Readable | Notify; + *(fieldPointer(m_data) + p->propertyData + p->propertyCount * PROPERTY_FIELD_COUNT + index) = notifySignal; + } +} + +void QFastMetaBuilder::setProperty(int index, const StringRef &name, const StringRef &type, + QFastMetaBuilder::PropertyFlag flags, int notifySignal) +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(!name.isEmpty() && !type.isEmpty()); + + QMetaObjectPrivate *p = priv(m_data); + Q_ASSERT(index < p->propertyCount); + + uint *ptr = fieldPointer(m_data) + p->propertyData + index * PROPERTY_FIELD_COUNT; + // properties: name, type, flags + ptr[0] = name.offset(); + ptr[1] = type.offset(); + if (notifySignal == -1) { + ptr[2] = flags | Scriptable | Readable; + *(fieldPointer(m_data) + p->propertyData + p->propertyCount * PROPERTY_FIELD_COUNT + index) = 0; + } else { + ptr[2] = flags | Scriptable | Readable | Notify; + *(fieldPointer(m_data) + p->propertyData + p->propertyCount * PROPERTY_FIELD_COUNT + index) = notifySignal; + } +} + +void QFastMetaBuilder::setSignal(int index, const StringRef &signature, + const StringRef ¶meterNames, + const StringRef &type) +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(!signature.isEmpty()); + + QMetaObjectPrivate *p = priv(m_data); + int mindex = metaObjectIndexForSignal(index); + + uint *ptr = fieldPointer(m_data) + p->methodData + mindex * METHOD_FIELD_COUNT; + // methods: signature, parameters, type, tag, flags + ptr[0] = signature.offset(); + ptr[1] = parameterNames.isEmpty()?m_zeroPtr:parameterNames.offset(); + ptr[2] = type.isEmpty()?m_zeroPtr:type.offset(); + ptr[3] = m_zeroPtr; + ptr[4] = AccessProtected | MethodSignal; +} + +void QFastMetaBuilder::setMethod(int index, const StringRef &signature, + const StringRef ¶meterNames, + const StringRef &type) +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(!signature.isEmpty()); + + QMetaObjectPrivate *p = priv(m_data); + int mindex = metaObjectIndexForMethod(index); + + uint *ptr = fieldPointer(m_data) + p->methodData + mindex * METHOD_FIELD_COUNT; + // methods: signature, parameters, type, tag, flags + ptr[0] = signature.offset(); + ptr[1] = parameterNames.isEmpty()?m_zeroPtr:parameterNames.offset(); + ptr[2] = type.isEmpty()?m_zeroPtr:type.offset(); + ptr[3] = m_zeroPtr; + ptr[4] = AccessProtected | MethodSlot; +} + +int QFastMetaBuilder::metaObjectIndexForSignal(int index) const +{ + Q_ASSERT(!m_data.isEmpty()); + Q_ASSERT(index < priv(m_data)->signalCount); + return index; +} + +int QFastMetaBuilder::metaObjectIndexForMethod(int index) const +{ + Q_ASSERT(!m_data.isEmpty()); + + const QMetaObjectPrivate *p = priv(m_data); + Q_ASSERT(index < (p->methodCount - p->signalCount)); + return index + p->signalCount; +} + +void QFastMetaBuilder::allocateStringData() +{ + if (m_stringDataAllocated < m_stringDataLength) { + m_data.resize(m_data.size() + m_stringDataLength - m_stringDataAllocated); + m_stringDataAllocated = m_stringDataLength; + m_stringData = m_data.data() + header(m_data)->fieldCount * sizeof(uint); + } +} + +void QFastMetaBuilder::fromData(QMetaObject *output, const QMetaObject *parent, const QByteArray &data) +{ + output->d.superdata = parent; + output->d.stringdata = data.constData() + header(data)->fieldCount * sizeof(uint); + output->d.data = fieldPointer(data); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qfastmetabuilder_p.h b/src/qml/qml/ftw/qfastmetabuilder_p.h new file mode 100644 index 0000000000..c1f6a3de5c --- /dev/null +++ b/src/qml/qml/ftw/qfastmetabuilder_p.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFASTMETABUILDER_P_H +#define QFASTMETABUILDER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of moc. This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qmetaobject.h> + +#include <private/qhashedstring_p.h> + +QT_BEGIN_NAMESPACE + +struct QMetaObject; +class QFastMetaBuilder +{ +public: + QFastMetaBuilder(); + ~QFastMetaBuilder(); + + struct StringRef { + public: + inline StringRef(); + inline StringRef(const StringRef &); + inline StringRef &operator=(const StringRef &); + + inline void load(const QHashedStringRef &); + inline void load(const QByteArray &); + inline void load(const char *); + + inline bool isEmpty() const; + inline QFastMetaBuilder *builder() const; + inline int offset() const; + inline char *data(); + inline int length() const; + private: + friend class QFastMetaBuilder; + + QFastMetaBuilder *_b; + int _o; + int _l; + }; + StringRef newString(int length); + + // Returns class name + StringRef init(int classNameLength, + int propertyCount, int methodCount, + int signalCount, int classInfoCount); + + void setClassInfo(int index, const StringRef &key, const StringRef &value); + + enum PropertyFlag { + None = 0x00000000, + Writable = 0x00000002, + Resettable = 0x00000004, + Constant = 0x00000400, + Final = 0x00000800 + }; + // void setProperty(int index, const StringRef &name, QMetaType::Type type, int notifySignal = -1); + void setProperty(int index, const StringRef &name, const StringRef &type, + QMetaType::Type mtype, PropertyFlag flags, int notifySignal = -1); + void setProperty(int index, const StringRef &name, const StringRef &type, + PropertyFlag flags, int notifySignal = -1); + void setMethod(int index, const StringRef &signature, + const StringRef ¶meterNames = StringRef(), + const StringRef &type = StringRef()); + void setSignal(int index, const StringRef &signature, + const StringRef ¶meterNames = StringRef(), + const StringRef &type = StringRef()); + + int metaObjectIndexForSignal(int) const; + int metaObjectIndexForMethod(int) const; + + QByteArray toData() const { return m_data; } + static void fromData(QMetaObject *, const QMetaObject *parent, const QByteArray &); +private: + friend struct StringRef; + + QByteArray m_data; + int m_zeroPtr; + + void allocateStringData(); + char *m_stringData; + int m_stringDataLength; + int m_stringDataAllocated; +}; + +QFastMetaBuilder::StringRef::StringRef() +: _b(0), _o(0), _l(0) +{ +} + +QFastMetaBuilder::StringRef::StringRef(const StringRef &o) +: _b(o._b), _o(o._o), _l(o._l) +{ +} + +QFastMetaBuilder::StringRef &QFastMetaBuilder::StringRef::operator=(const StringRef &o) +{ + _b = o._b; + _o = o._o; + _l = o._l; + return *this; +} + +bool QFastMetaBuilder::StringRef::isEmpty() const +{ + return _l == 0; +} + +QFastMetaBuilder *QFastMetaBuilder::StringRef::builder() const +{ + return _b; +} + +int QFastMetaBuilder::StringRef::offset() const +{ + return _o; +} + +char *QFastMetaBuilder::StringRef::data() +{ + Q_ASSERT(_b); + if (_b->m_stringDataLength != _b->m_stringDataAllocated) + _b->allocateStringData(); + return _b->m_stringData + _o; +} + +int QFastMetaBuilder::StringRef::length() const +{ + return _l; +} + +void QFastMetaBuilder::StringRef::load(const QHashedStringRef &str) +{ + Q_ASSERT(str.utf8length() == _l); + str.writeUtf8(data()); + *(data() + _l) = 0; +} + +void QFastMetaBuilder::StringRef::load(const QByteArray &str) +{ + Q_ASSERT(str.length() == _l); + strcpy(data(), str.constData()); +} + +void QFastMetaBuilder::StringRef::load(const char *str) +{ + Q_ASSERT(strlen(str) == (uint)_l); + strcpy(data(), str); +} + +QT_END_NAMESPACE + +#endif // QFASTMETABUILDER_P_H + diff --git a/src/qml/qml/ftw/qfieldlist_p.h b/src/qml/qml/ftw/qfieldlist_p.h new file mode 100644 index 0000000000..da5074b3cd --- /dev/null +++ b/src/qml/qml/ftw/qfieldlist_p.h @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFIELDLIST_P_H +#define QFIELDLIST_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 <private/qflagpointer_p.h> + +// QForwardFieldList is a super simple linked list that can only prepend +template<class N, N *N::*nextMember> +class QForwardFieldList +{ +public: + inline QForwardFieldList(); + inline N *first() const; + inline N *takeFirst(); + + inline void prepend(N *); + + inline bool isEmpty() const; + inline bool isOne() const; + inline bool isMany() const; + + static inline N *next(N *v); + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); + + inline bool flag2() const; + inline void setFlag2(); + inline void clearFlag2(); + inline void setFlag2Value(bool); +private: + QFlagPointer<N> _first; +}; + +// QFieldList is a simple linked list, that can append and prepend and also +// maintains a count +template<class N, N *N::*nextMember> +class QFieldList +{ +public: + inline QFieldList(); + inline N *first() const; + inline N *takeFirst(); + + inline void append(N *); + inline void prepend(N *); + + inline bool isEmpty() const; + inline bool isOne() const; + inline bool isMany() const; + inline int count() const; + + inline void append(QFieldList<N, nextMember> &); + inline void prepend(QFieldList<N, nextMember> &); + inline void insertAfter(N *, QFieldList<N, nextMember> &); + + inline void copyAndClear(QFieldList<N, nextMember> &); + inline void copyAndClearAppend(QForwardFieldList<N, nextMember> &); + inline void copyAndClearPrepend(QForwardFieldList<N, nextMember> &); + + static inline N *next(N *v); + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); +private: + N *_first; + N *_last; + quint32 _flag:1; + quint32 _count:31; +}; + +template<class N, N *N::*nextMember> +QForwardFieldList<N, nextMember>::QForwardFieldList() +{ +} + +template<class N, N *N::*nextMember> +N *QForwardFieldList<N, nextMember>::first() const +{ + return *_first; +} + +template<class N, N *N::*nextMember> +N *QForwardFieldList<N, nextMember>::takeFirst() +{ + N *value = *_first; + if (value) { + _first = next(value); + value->*nextMember = 0; + } + return value; +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::prepend(N *v) +{ + Q_ASSERT(v->*nextMember == 0); + v->*nextMember = *_first; + _first = v; +} + +template<class N, N *N::*nextMember> +bool QForwardFieldList<N, nextMember>::isEmpty() const +{ + return _first.isNull(); +} + +template<class N, N *N::*nextMember> +bool QForwardFieldList<N, nextMember>::isOne() const +{ + return *_first && _first->*nextMember == 0; +} + +template<class N, N *N::*nextMember> +bool QForwardFieldList<N, nextMember>::isMany() const +{ + return *_first && _first->*nextMember != 0; +} + +template<class N, N *N::*nextMember> +N *QForwardFieldList<N, nextMember>::next(N *v) +{ + Q_ASSERT(v); + return v->*nextMember; +} + +template<class N, N *N::*nextMember> +bool QForwardFieldList<N, nextMember>::flag() const +{ + return _first.flag(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::setFlag() +{ + _first.setFlag(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::clearFlag() +{ + _first.clearFlag(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::setFlagValue(bool v) +{ + _first.setFlagValue(v); +} + +template<class N, N *N::*nextMember> +bool QForwardFieldList<N, nextMember>::flag2() const +{ + return _first.flag2(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::setFlag2() +{ + _first.setFlag2(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::clearFlag2() +{ + _first.clearFlag2(); +} + +template<class N, N *N::*nextMember> +void QForwardFieldList<N, nextMember>::setFlag2Value(bool v) +{ + _first.setFlag2Value(v); +} + +template<class N, N *N::*nextMember> +QFieldList<N, nextMember>::QFieldList() +: _first(0), _last(0), _flag(0), _count(0) +{ +} + +template<class N, N *N::*nextMember> +N *QFieldList<N, nextMember>::first() const +{ + return _first; +} + +template<class N, N *N::*nextMember> +N *QFieldList<N, nextMember>::takeFirst() +{ + N *value = _first; + if (value) { + _first = next(value); + if (_last == value) { + Q_ASSERT(_first == 0); + _last = 0; + } + value->*nextMember = 0; + --_count; + } + return value; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::append(N *v) +{ + Q_ASSERT(v->*nextMember == 0); + if (isEmpty()) { + _first = v; + _last = v; + } else { + _last->*nextMember = v; + _last = v; + } + ++_count; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::prepend(N *v) +{ + Q_ASSERT(v->*nextMember == 0); + if (isEmpty()) { + _first = v; + _last = v; + } else { + v->*nextMember = _first; + _first = v; + } + ++_count; +} + +template<class N, N *N::*nextMember> +bool QFieldList<N, nextMember>::isEmpty() const +{ + return _count == 0; +} + +template<class N, N *N::*nextMember> +bool QFieldList<N, nextMember>::isOne() const +{ + return _count == 1; +} + +template<class N, N *N::*nextMember> +bool QFieldList<N, nextMember>::isMany() const +{ + return _count > 1; +} + +template<class N, N *N::*nextMember> +int QFieldList<N, nextMember>::count() const +{ + return _count; +} + +template<class N, N *N::*nextMember> +N *QFieldList<N, nextMember>::next(N *v) +{ + Q_ASSERT(v); + return v->*nextMember; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::append(QFieldList<N, nextMember> &o) +{ + if (!o.isEmpty()) { + if (isEmpty()) { + _first = o._first; + _last = o._last; + _count = o._count; + } else { + _last->*nextMember = o._first; + _last = o._last; + _count += o._count; + } + o._first = o._last = 0; o._count = 0; + } +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::prepend(QFieldList<N, nextMember> &o) +{ + if (!o.isEmpty()) { + if (isEmpty()) { + _first = o._first; + _last = o._last; + _count = o._count; + } else { + o._last->*nextMember = _first; + _first = o._first; + _count += o._count; + } + o._first = o._last = 0; o._count = 0; + } +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::insertAfter(N *after, QFieldList<N, nextMember> &o) +{ + if (after == 0) { + prepend(o); + } else if (after == _last) { + append(o); + } else if (!o.isEmpty()) { + if (isEmpty()) { + _first = o._first; + _last = o._last; + _count = o._count; + } else { + o._last->*nextMember = after->*nextMember; + after->*nextMember = o._first; + _count += o._count; + } + o._first = o._last = 0; o._count = 0; + } +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::copyAndClear(QFieldList<N, nextMember> &o) +{ + _first = o._first; + _last = o._last; + _count = o._count; + o._first = o._last = 0; + o._count = 0; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::copyAndClearAppend(QForwardFieldList<N, nextMember> &o) +{ + _first = 0; + _last = 0; + _count = 0; + while (N *n = o.takeFirst()) append(n); +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::copyAndClearPrepend(QForwardFieldList<N, nextMember> &o) +{ + _first = 0; + _last = 0; + _count = 0; + while (N *n = o.takeFirst()) prepend(n); +} + +template<class N, N *N::*nextMember> +bool QFieldList<N, nextMember>::flag() const +{ + return _flag; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::setFlag() +{ + _flag = true; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::clearFlag() +{ + _flag = false; +} + +template<class N, N *N::*nextMember> +void QFieldList<N, nextMember>::setFlagValue(bool v) +{ + _flag = v; +} + +#endif // QFIELDLIST_P_H diff --git a/src/qml/qml/ftw/qfinitestack_p.h b/src/qml/qml/ftw/qfinitestack_p.h new file mode 100644 index 0000000000..c5a9fbaedb --- /dev/null +++ b/src/qml/qml/ftw/qfinitestack_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFINITESTACK_P_H +#define QFINITESTACK_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> + +QT_BEGIN_NAMESPACE + +template<typename T> +struct QFiniteStack { + inline QFiniteStack(); + inline ~QFiniteStack(); + + inline void deallocate(); + inline void allocate(int size); + + inline bool isEmpty() const; + inline const T &top() const; + inline T &top(); + inline void push(const T &o); + inline T pop(); + inline int count() const; + inline const T &at(int index) const; + inline T &operator[](int index); +private: + T *_array; + int _alloc; + int _size; +}; + +template<typename T> +QFiniteStack<T>::QFiniteStack() +: _array(0), _alloc(0), _size(0) +{ +} + +template<typename T> +QFiniteStack<T>::~QFiniteStack() +{ + deallocate(); +} + +template<typename T> +bool QFiniteStack<T>::isEmpty() const +{ + return _size == 0; +} + +template<typename T> +const T &QFiniteStack<T>::top() const +{ + return _array[_size - 1]; +} + +template<typename T> +T &QFiniteStack<T>::top() +{ + return _array[_size - 1]; +} + +template<typename T> +void QFiniteStack<T>::push(const T &o) +{ + if (QTypeInfo<T>::isComplex) { + new (_array + _size++) T(o); + } else { + _array[_size++] = o; + } +} + +template<typename T> +T QFiniteStack<T>::pop() +{ + --_size; + + if (QTypeInfo<T>::isComplex) { + T rv = _array[_size]; + (_array + _size)->~T(); + return rv; + } else { + return _array[_size]; + } +} + +template<typename T> +int QFiniteStack<T>::count() const +{ + return _size; +} + +template<typename T> +const T &QFiniteStack<T>::at(int index) const +{ + return _array[index]; +} + +template<typename T> +T &QFiniteStack<T>::operator[](int index) +{ + return _array[index]; +} + +template<typename T> +void QFiniteStack<T>::allocate(int size) +{ + Q_ASSERT(_array == 0); + Q_ASSERT(_alloc == 0); + Q_ASSERT(_size == 0); + + if (!size) return; + + _array = (T *)qMalloc(size * sizeof(T)); + _alloc = size; +} + +template<typename T> +void QFiniteStack<T>::deallocate() +{ + if (QTypeInfo<T>::isComplex) { + T *i = _array + _size; + while (i != _array) + (--i)->~T(); + } + + qFree(_array); + + _array = 0; + _alloc = 0; + _size = 0; +} + +QT_END_NAMESPACE + +#endif // QFINITESTACK_P_H + diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h new file mode 100644 index 0000000000..a4b20d9e2a --- /dev/null +++ b/src/qml/qml/ftw/qflagpointer_p.h @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFLAGPOINTER_P_H +#define QFLAGPOINTER_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> + +QT_BEGIN_NAMESPACE + +template<typename T> +class QFlagPointer { +public: + inline QFlagPointer(); + inline QFlagPointer(T *); + inline QFlagPointer(const QFlagPointer<T> &o); + + inline bool isNull() const; + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); + + inline bool flag2() const; + inline void setFlag2(); + inline void clearFlag2(); + inline void setFlag2Value(bool); + + inline QFlagPointer<T> &operator=(const QFlagPointer &o); + inline QFlagPointer<T> &operator=(T *); + + inline T *operator->() const; + inline T *operator*() const; + +private: + quintptr ptr_value; + + static const quintptr FlagBit = 0x1; + static const quintptr Flag2Bit = 0x2; + static const quintptr FlagsMask = FlagBit | Flag2Bit; +}; + +template<typename T, typename T2> +class QBiPointer { +public: + inline QBiPointer(); + inline QBiPointer(T *); + inline QBiPointer(T2 *); + inline QBiPointer(const QBiPointer<T, T2> &o); + + inline bool isNull() const; + inline bool isT1() const; + inline bool isT2() const; + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); + + inline QBiPointer<T, T2> &operator=(const QBiPointer<T, T2> &o); + inline QBiPointer<T, T2> &operator=(T *); + inline QBiPointer<T, T2> &operator=(T2 *); + + inline T *asT1() const; + inline T2 *asT2() const; + +private: + quintptr ptr_value; + + static const quintptr FlagBit = 0x1; + static const quintptr Flag2Bit = 0x2; + static const quintptr FlagsMask = FlagBit | Flag2Bit; +}; + +template<typename T> +QFlagPointer<T>::QFlagPointer() +: ptr_value(0) +{ +} + +template<typename T> +QFlagPointer<T>::QFlagPointer(T *v) +: ptr_value(quintptr(v)) +{ + Q_ASSERT((ptr_value & FlagsMask) == 0); +} + +template<typename T> +QFlagPointer<T>::QFlagPointer(const QFlagPointer<T> &o) +: ptr_value(o.ptr_value) +{ +} + +template<typename T> +bool QFlagPointer<T>::isNull() const +{ + return 0 == (ptr_value & (~FlagsMask)); +} + +template<typename T> +bool QFlagPointer<T>::flag() const +{ + return ptr_value & FlagBit; +} + +template<typename T> +void QFlagPointer<T>::setFlag() +{ + ptr_value |= FlagBit; +} + +template<typename T> +void QFlagPointer<T>::clearFlag() +{ + ptr_value &= ~FlagBit; +} + +template<typename T> +void QFlagPointer<T>::setFlagValue(bool v) +{ + if (v) setFlag(); + else clearFlag(); +} + +template<typename T> +bool QFlagPointer<T>::flag2() const +{ + return ptr_value & Flag2Bit; +} + +template<typename T> +void QFlagPointer<T>::setFlag2() +{ + ptr_value|= Flag2Bit; +} + +template<typename T> +void QFlagPointer<T>::clearFlag2() +{ + ptr_value &= ~Flag2Bit; +} + +template<typename T> +void QFlagPointer<T>::setFlag2Value(bool v) +{ + if (v) setFlag2(); + else clearFlag2(); +} + +template<typename T> +QFlagPointer<T> &QFlagPointer<T>::operator=(const QFlagPointer &o) +{ + ptr_value = o.ptr_value; + return *this; +} + +template<typename T> +QFlagPointer<T> &QFlagPointer<T>::operator=(T *o) +{ + Q_ASSERT((quintptr(o) & FlagsMask) == 0); + + ptr_value = quintptr(o) | (ptr_value & FlagsMask); + return *this; +} + +template<typename T> +T *QFlagPointer<T>::operator->() const +{ + return (T *)(ptr_value & ~FlagsMask); +} + +template<typename T> +T *QFlagPointer<T>::operator*() const +{ + return (T *)(ptr_value & ~FlagsMask); +} + +template<typename T, typename T2> +QBiPointer<T, T2>::QBiPointer() +: ptr_value(0) +{ +} + +template<typename T, typename T2> +QBiPointer<T, T2>::QBiPointer(T *v) +: ptr_value(quintptr(v)) +{ + Q_ASSERT((quintptr(v) & FlagsMask) == 0); +} + +template<typename T, typename T2> +QBiPointer<T, T2>::QBiPointer(T2 *v) +: ptr_value(quintptr(v) | Flag2Bit) +{ + Q_ASSERT((quintptr(v) & FlagsMask) == 0); +} + +template<typename T, typename T2> +QBiPointer<T, T2>::QBiPointer(const QBiPointer<T, T2> &o) +: ptr_value(o.ptr_value) +{ +} + +template<typename T, typename T2> +bool QBiPointer<T, T2>::isNull() const +{ + return 0 == (ptr_value & (~FlagsMask)); +} + +template<typename T, typename T2> +bool QBiPointer<T, T2>::isT1() const +{ + return !(ptr_value & Flag2Bit); +} + +template<typename T, typename T2> +bool QBiPointer<T, T2>::isT2() const +{ + return ptr_value & Flag2Bit; +} + +template<typename T, typename T2> +bool QBiPointer<T, T2>::flag() const +{ + return ptr_value & FlagBit; +} + +template<typename T, typename T2> +void QBiPointer<T, T2>::setFlag() +{ + ptr_value |= FlagBit; +} + +template<typename T, typename T2> +void QBiPointer<T, T2>::clearFlag() +{ + ptr_value &= ~FlagBit; +} + +template<typename T, typename T2> +void QBiPointer<T, T2>::setFlagValue(bool v) +{ + if (v) setFlag(); + else clearFlag(); +} + +template<typename T, typename T2> +QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(const QBiPointer<T, T2> &o) +{ + ptr_value = o.ptr_value; + return *this; +} + +template<typename T, typename T2> +QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T *o) +{ + Q_ASSERT((quintptr(o) & FlagsMask) == 0); + + ptr_value = quintptr(o) | (ptr_value & FlagBit); + return *this; +} + +template<typename T, typename T2> +QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T2 *o) +{ + Q_ASSERT((quintptr(o) & FlagsMask) == 0); + + ptr_value = quintptr(o) | (ptr_value & FlagBit) | Flag2Bit; + return *this; +} + +template<typename T, typename T2> +T *QBiPointer<T, T2>::asT1() const +{ + Q_ASSERT(isT1()); + return (T *)(ptr_value & ~FlagsMask); +} + +template<typename T, typename T2> +T2 *QBiPointer<T, T2>::asT2() const +{ + Q_ASSERT(isT2()); + return (T2 *)(ptr_value & ~FlagsMask); +} + +QT_END_NAMESPACE + +#endif // QFLAGPOINTER_P_H diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp new file mode 100644 index 0000000000..1f09d50ed3 --- /dev/null +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -0,0 +1,490 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhashedstring_p.h" + +// This is a reimplementation of V8's string hash algorithm. It is significantly +// faster to do it here than call into V8, but it adds the maintainence burden of +// ensuring that the two hashes are identical. We Q_ASSERT() that the two return +// the same value. If these asserts start to fail, the hash code needs to be +// synced with V8. +namespace String { + static const int kMaxArrayIndexSize = 10; + static const int kMaxHashCalcLength = 16383; + static const int kNofHashBitFields = 2; + static const int kHashShift = kNofHashBitFields; + static const int kIsNotArrayIndexMask = 1 << 1; + static const int kArrayIndexValueBits = 24; + static const int kArrayIndexHashLengthShift = kArrayIndexValueBits + kNofHashBitFields; + static const int kMaxCachedArrayIndexLength = 7; +}; + +template <typename schar> +uint32_t calculateHash(const schar* chars, int length) { + if (length > String::kMaxHashCalcLength) { + // V8 trivial hash + return (length << String::kHashShift) | String::kIsNotArrayIndexMask; + } + + uint32_t raw_running_hash = 0; + uint32_t array_index = 0; + bool is_array_index = (0 < length && length <= String::kMaxArrayIndexSize); + bool is_first_char = true; + + int ii = 0; + for (;is_array_index && ii < length; ++ii) { + quint32 c = *chars++; + + raw_running_hash += c; + raw_running_hash += (raw_running_hash << 10); + raw_running_hash ^= (raw_running_hash >> 6); + + if (c < '0' || c > '9') { + is_array_index = false; + } else { + int d = c - '0'; + if (is_first_char) { + is_first_char = false; + if (c == '0' && length > 1) { + is_array_index = false; + continue; + } + } + if (array_index > 429496729U - ((d + 2) >> 3)) { + is_array_index = false; + } else { + array_index = array_index * 10 + d; + } + } + } + + for (;ii < length; ++ii) { + raw_running_hash += *chars++; + raw_running_hash += (raw_running_hash << 10); + raw_running_hash ^= (raw_running_hash >> 6); + } + + if (is_array_index) { + array_index <<= String::kHashShift; + array_index |= length << String::kArrayIndexHashLengthShift; + return array_index; + } else { + raw_running_hash += (raw_running_hash << 3); + raw_running_hash ^= (raw_running_hash >> 11); + raw_running_hash += (raw_running_hash << 15); + if (raw_running_hash == 0) { + raw_running_hash = 27; + } + + return (raw_running_hash << String::kHashShift) | String::kIsNotArrayIndexMask; + } +} + +inline quint32 stringHash(const QChar* data, int length) +{ + quint32 rv = calculateHash<quint16>((quint16*)data, length) >> String::kHashShift; + Q_ASSERT(rv == v8::String::ComputeHash((uint16_t*)data, length)); + return rv; +} + +inline quint32 stringHash(const char *data, int length) +{ + quint32 rv = calculateHash<quint8>((quint8*)data, length) >> String::kHashShift; + Q_ASSERT(rv == v8::String::ComputeHash((char *)data, length)); + return rv; +} + +void QHashedString::computeHash() const +{ + m_hash = stringHash(constData(), length()); +} + +void QHashedStringRef::computeHash() const +{ + m_hash = stringHash(m_data, m_length); +} + +void QHashedCStringRef::computeHash() const +{ + m_hash = stringHash(m_data, m_length); +} + +/* + 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, IteratorData first, + IteratorData (*Iterate)(const IteratorData &), + QStringHashNode *skip) +{ + short bits = qMax(MinNumBits, (int)numBits); + while (primeForNumBits(bits) < size) bits++; + + if (bits > numBits) + rehashToBits(bits, first, Iterate, skip); +} + +void QStringHashData::rehashToBits(short bits, IteratorData first, + IteratorData (*Iterate)(const IteratorData &), + QStringHashNode *skip) +{ + numBits = qMax(MinNumBits, (int)bits); + + int nb = primeForNumBits(numBits); + if (nb == numBuckets && buckets) + return; + + numBuckets = nb; + +#ifdef QSTRINGHASH_LINK_DEBUG + if (linkCount) + qFatal("QStringHash: Illegal attempt to rehash a linked hash."); +#endif + + delete [] buckets; + buckets = new QStringHashNode *[numBuckets]; + ::memset(buckets, 0, sizeof(QStringHashNode *) * numBuckets); + + IteratorData nodeList = first; + while (nodeList.n) { + if (nodeList.n != skip) { + int bucket = nodeList.n->hash % numBuckets; + nodeList.n->next = buckets[bucket]; + buckets[bucket] = nodeList.n; + } + + nodeList = Iterate(nodeList); + } +} + +// Copy of QString's qMemCompare +bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) +{ + Q_ASSERT(lhs && rhs); + const quint16 *a = (const quint16 *)lhs; + const quint16 *b = (const quint16 *)rhs; + + if (a == b || !length) + return true; + + register union { + const quint16 *w; + const quint32 *d; + quintptr value; + } sa, sb; + sa.w = a; + sb.w = b; + + // check alignment + if ((sa.value & 2) == (sb.value & 2)) { + // both addresses have the same alignment + if (sa.value & 2) { + // both addresses are not aligned to 4-bytes boundaries + // compare the first character + if (*sa.w != *sb.w) + return false; + --length; + ++sa.w; + ++sb.w; + + // now both addresses are 4-bytes aligned + } + + // both addresses are 4-bytes aligned + // do a fast 32-bit comparison + register const quint32 *e = sa.d + (length >> 1); + for ( ; sa.d != e; ++sa.d, ++sb.d) { + if (*sa.d != *sb.d) + return false; + } + + // do we have a tail? + return (length & 1) ? *sa.w == *sb.w : true; + } else { + // one of the addresses isn't 4-byte aligned but the other is + register const quint16 *e = sa.w + length; + for ( ; sa.w != e; ++sa.w, ++sb.w) { + if (*sa.w != *sb.w) + return false; + } + } + return true; +} + +// Unicode stuff +static inline bool isUnicodeNonCharacter(uint ucs4) +{ + // Unicode has a couple of "non-characters" that one can use internally, + // but are not allowed to be used for text interchange. + // + // Those are the last two entries each Unicode Plane (U+FFFE, U+FFFF, + // U+1FFFE, U+1FFFF, etc.) as well as the entries between U+FDD0 and + // U+FDEF (inclusive) + + return (ucs4 & 0xfffe) == 0xfffe + || (ucs4 - 0xfdd0U) < 16; +} + +static int utf8LengthFromUtf16(const QChar *uc, int len) +{ + int length = 0; + + int surrogate_high = -1; + + const QChar *ch = uc; + int invalid = 0; + + const QChar *end = ch + len; + while (ch < end) { + uint u = ch->unicode(); + if (surrogate_high >= 0) { + if (u >= 0xdc00 && u < 0xe000) { + u = (surrogate_high - 0xd800)*0x400 + (u - 0xdc00) + 0x10000; + surrogate_high = -1; + } else { + // high surrogate without low + ++ch; + ++invalid; + surrogate_high = -1; + continue; + } + } else if (u >= 0xdc00 && u < 0xe000) { + // low surrogate without high + ++ch; + ++invalid; + continue; + } else if (u >= 0xd800 && u < 0xdc00) { + surrogate_high = u; + ++ch; + continue; + } + + if (u < 0x80) { + ++length; + } else { + if (u < 0x0800) { + ++length; + } else { + // is it one of the Unicode non-characters? + if (isUnicodeNonCharacter(u)) { + ++length; + ++ch; + ++invalid; + continue; + } + + if (u > 0xffff) { + ++length; + ++length; + } else { + ++length; + } + ++length; + } + ++length; + } + ++ch; + } + + return length; +} + +// Writes the utf8 version of uc to output. uc is of length len. +// There must be at least utf8LengthFromUtf16(uc, len) bytes in output. +// A null terminator is not written. +static void utf8FromUtf16(char *output, const QChar *uc, int len) +{ + uchar replacement = '?'; + int surrogate_high = -1; + + uchar* cursor = (uchar*)output; + const QChar *ch = uc; + int invalid = 0; + + const QChar *end = ch + len; + while (ch < end) { + uint u = ch->unicode(); + if (surrogate_high >= 0) { + if (u >= 0xdc00 && u < 0xe000) { + u = (surrogate_high - 0xd800)*0x400 + (u - 0xdc00) + 0x10000; + surrogate_high = -1; + } else { + // high surrogate without low + *cursor = replacement; + ++ch; + ++invalid; + surrogate_high = -1; + continue; + } + } else if (u >= 0xdc00 && u < 0xe000) { + // low surrogate without high + *cursor = replacement; + ++ch; + ++invalid; + continue; + } else if (u >= 0xd800 && u < 0xdc00) { + surrogate_high = u; + ++ch; + continue; + } + + if (u < 0x80) { + *cursor++ = (uchar)u; + } else { + if (u < 0x0800) { + *cursor++ = 0xc0 | ((uchar) (u >> 6)); + } else { + // is it one of the Unicode non-characters? + if (isUnicodeNonCharacter(u)) { + *cursor++ = replacement; + ++ch; + ++invalid; + continue; + } + + if (u > 0xffff) { + *cursor++ = 0xf0 | ((uchar) (u >> 18)); + *cursor++ = 0x80 | (((uchar) (u >> 12)) & 0x3f); + } else { + *cursor++ = 0xe0 | (((uchar) (u >> 12)) & 0x3f); + } + *cursor++ = 0x80 | (((uchar) (u >> 6)) & 0x3f); + } + *cursor++ = 0x80 | ((uchar) (u&0x3f)); + } + ++ch; + } +} + +void QHashedStringRef::computeUtf8Length() const +{ + if (m_length) + m_utf8length = utf8LengthFromUtf16(m_data, m_length); + else + m_utf8length = 0; +} + +QHashedStringRef QHashedStringRef::mid(int offset, int length) const +{ + Q_ASSERT(offset < m_length); + return QHashedStringRef(m_data + offset, + (length == -1 || (offset + length) > m_length)?(m_length - offset):length); +} + +bool QHashedStringRef::endsWith(const QString &s) const +{ + return s.length() < m_length && + QHashedString::compare(s.constData(), m_data + m_length - s.length(), s.length()); +} + +bool QHashedStringRef::startsWith(const QString &s) const +{ + return s.length() < m_length && + QHashedString::compare(s.constData(), m_data, s.length()); +} + +QString QHashedStringRef::toString() const +{ + if (m_length == 0) + return QString(); + return QString(m_data, m_length); +} + +QByteArray QHashedStringRef::toUtf8() const +{ + if (m_length == 0) + return QByteArray(); + + QByteArray result; + result.resize(utf8length()); + writeUtf8(result.data()); + return result; +} + +void QHashedStringRef::writeUtf8(char *output) const +{ + if (m_length) { + int ulen = utf8length(); + if (ulen == m_length) { + // Must be a latin1 string + uchar *o = (uchar *)output; + const QChar *c = m_data; + while (ulen--) + *o++ = (uchar)((*c++).unicode()); + } else { + utf8FromUtf16(output, m_data, m_length); + } + } +} + +QString QHashedCStringRef::toUtf16() const +{ + if (m_length == 0) + return QString(); + + QString rv; + rv.resize(m_length); + writeUtf16((uint16_t*)rv.data()); + return rv; +} + diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h new file mode 100644 index 0000000000..f575285ff6 --- /dev/null +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -0,0 +1,1418 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHASHEDSTRING_P_H +#define QHASHEDSTRING_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/qstring.h> +#include <private/qv8_p.h> + +#include <private/qflagpointer_p.h> + +QT_BEGIN_NAMESPACE + +// Enable this to debug hash linking assumptions. +// #define QSTRINGHASH_LINK_DEBUG + +class QHashedStringRef; +class Q_AUTOTEST_EXPORT QHashedString : public QString +{ +public: + inline QHashedString(); + inline QHashedString(const QString &string); + inline QHashedString(const QString &string, quint32); + inline QHashedString(const QHashedString &string); + + inline QHashedString &operator=(const QHashedString &string); + inline bool operator==(const QHashedString &string) const; + inline bool operator==(const QHashedStringRef &string) const; + + inline quint32 hash() const; + inline quint32 existingHash() const; + + static inline bool isUpper(const QChar &); + + static bool compare(const QChar *lhs, const QChar *rhs, int length); + static inline bool compare(const QChar *lhs, const char *rhs, int length); + static inline bool compare(const char *lhs, const char *rhs, int length); +private: + friend class QHashedStringRef; + friend class QStringHashNode; + + void computeHash() const; + mutable quint32 m_hash; +}; + +class Q_AUTOTEST_EXPORT QHashedV8String +{ +public: + inline QHashedV8String(); + explicit inline QHashedV8String(v8::Handle<v8::String>); + inline QHashedV8String(const QHashedV8String &string); + inline QHashedV8String &operator=(const QHashedV8String &other); + + inline bool operator==(const QHashedV8String &string); + + inline quint32 hash() const; + inline int length() const; + inline quint32 symbolId() const; + + inline v8::Handle<v8::String> string() const; + + inline QString toString() const; + +private: + v8::String::CompleteHashData m_hash; + v8::Handle<v8::String> m_string; +}; + +class QHashedCStringRef; +class Q_AUTOTEST_EXPORT QHashedStringRef +{ +public: + inline QHashedStringRef(); + inline QHashedStringRef(const QString &); + inline QHashedStringRef(const QStringRef &); + inline QHashedStringRef(const QChar *, int); + inline QHashedStringRef(const QChar *, int, quint32); + inline QHashedStringRef(const QHashedString &); + inline QHashedStringRef(const QHashedStringRef &); + inline QHashedStringRef &operator=(const QHashedStringRef &); + + inline bool operator==(const QString &string) const; + inline bool operator==(const QHashedString &string) const; + inline bool operator==(const QHashedStringRef &string) const; + inline bool operator==(const QHashedCStringRef &string) const; + inline bool operator!=(const QString &string) const; + inline bool operator!=(const QHashedString &string) const; + inline bool operator!=(const QHashedStringRef &string) const; + inline bool operator!=(const QHashedCStringRef &string) const; + + inline quint32 hash() const; + + inline const QChar &at(int) const; + inline const QChar *constData() const; + bool startsWith(const QString &) const; + bool endsWith(const QString &) const; + QHashedStringRef mid(int, int) const; + + inline bool isEmpty() const; + inline int length() const; + inline bool startsWithUpper() const; + + QString toString() const; + + inline int utf8length() const; + QByteArray toUtf8() const; + void writeUtf8(char *) const; +private: + friend class QHashedString; + + void computeHash() const; + void computeUtf8Length() const; + + const QChar *m_data; + int m_length; + mutable int m_utf8length; + mutable quint32 m_hash; +}; + +class Q_AUTOTEST_EXPORT QHashedCStringRef +{ +public: + inline QHashedCStringRef(); + inline QHashedCStringRef(const char *, int); + inline QHashedCStringRef(const char *, int, quint32); + inline QHashedCStringRef(const QHashedCStringRef &); + + inline quint32 hash() const; + + inline const char *constData() const; + inline int length() const; + + QString toUtf16() const; + inline int utf16length() const; + inline void writeUtf16(QChar *) const; + inline void writeUtf16(uint16_t *) const; +private: + friend class QHashedStringRef; + + void computeHash() const; + + const char *m_data; + int m_length; + mutable quint32 m_hash; +}; + +class QStringHashData; +class Q_AUTOTEST_EXPORT QStringHashNode +{ +public: + QStringHashNode() + : length(0), hash(0), symbolId(0), ckey(0) + { + } + + 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; + quint32 hash; + quint32 symbolId; + + union { + const char *ckey; + QStringData *strData; + }; + + 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 uint16_t *utf16Data() const { return (uint16_t *)strData->data(); } + + inline bool equals(v8::Handle<v8::String> string) { + return isQString()?string->Equals(utf16Data(), length): + string->Equals(cStrData(), length); + } + + inline bool symbolEquals(const QHashedV8String &string) { + Q_ASSERT(string.symbolId() != 0); + return length == string.length() && hash == string.hash() && + (string.symbolId() == symbolId || equals(string.string())); + } + + inline bool equals(const QHashedV8String &string) { + return length == string.length() && hash == string.hash() && + equals(string.string()); + } + + inline bool equals(const QHashedStringRef &string) { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare(string.constData(), (QChar *)utf16Data(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } + + inline bool equals(const QHashedCStringRef &string) { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare((QChar *)utf16Data(), string.constData(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } +}; + +class Q_AUTOTEST_EXPORT QStringHashData +{ +public: + QStringHashData() + : buckets(0), numBuckets(0), size(0), numBits(0) +#ifdef QSTRINGHASH_LINK_DEBUG + , linkCount(0) +#endif + {} + + QStringHashNode **buckets; + int numBuckets; + int size; + short numBits; +#ifdef QSTRINGHASH_LINK_DEBUG + int linkCount; +#endif + + struct IteratorData { + IteratorData() : n(0), p(0) {} + QStringHashNode *n; + void *p; + }; + void rehashToBits(short, IteratorData, IteratorData (*Iterate)(const IteratorData &), + QStringHashNode *skip = 0); + void rehashToSize(int, IteratorData, IteratorData (*Iterate)(const IteratorData &), + QStringHashNode *skip = 0); + +private: + QStringHashData(const QStringHashData &); + QStringHashData &operator=(const QStringHashData &); +}; + +template<class T> +class QStringHash +{ +public: + 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(0) {} + NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(0) {} + NewedNode(const Node &o) : Node(o), nextNewed(0) {} + NewedNode *nextNewed; + }; + struct ReservedNodePool + { + ReservedNodePool() : count(0), used(0), nodes(0) {} + ~ReservedNodePool() { delete [] nodes; } + int count; + int used; + Node *nodes; + }; + + QStringHashData data; + NewedNode *newedNodes; + ReservedNodePool *nodePool; + const QStringHash<T> *link; + + inline Node *findNode(const QString &) const; + inline Node *findNode(const QHashedString &) const; + inline Node *findNode(const QHashedStringRef &) const; + inline Node *findNode(const QHashedCStringRef &) const; + inline Node *findNode(const QHashedV8String &) const; + inline Node *findSymbolNode(const QHashedV8String &) const; + inline Node *createNode(const Node &o); + inline Node *createNode(const QHashedString &, const T &); + inline Node *createNode(const QHashedCStringRef &, const T &); + + inline Node *takeNode(const QHashedString &key, const T &value); + inline Node *takeNode(const QHashedCStringRef &key, const T &value); + inline Node *takeNode(const Node &o); + + inline void copy(const QStringHash<T> &); + + 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; + + inline QHashedString key() const; + inline const T &value() const; + inline const T &operator*() const; + + inline Node *node() const; + private: + QStringHashData::IteratorData d; + }; + + inline void insert(const QString &, const T &); + inline void insert(const QHashedString &, const T &); + inline void insert(const QHashedStringRef &, const T &); + inline void insert(const QHashedCStringRef &, const T &); + inline void insert(const ConstIterator &); + + inline T *value(const QString &) const; + inline T *value(const QHashedString &) const; + inline T *value(const QHashedStringRef &) const; + inline T *value(const QHashedV8String &) const; + inline T *value(const QHashedCStringRef &) const; + inline T *value(const ConstIterator &) const; + + inline bool contains(const QString &) const; + inline bool contains(const QHashedString &) const; + inline bool contains(const QHashedStringRef &) const; + inline bool contains(const QHashedCStringRef &) const; + inline bool contains(const ConstIterator &) const; + + inline T &operator[](const QString &); + inline T &operator[](const QHashedString &); + inline T &operator[](const QHashedStringRef &); + inline T &operator[](const QHashedCStringRef &); + + inline ConstIterator begin() const; + inline ConstIterator end() const; + + inline void reserve(int); +}; + +template<class T> +QStringHash<T>::QStringHash() +: newedNodes(0), nodePool(0), link(0) +{ +} + +template<class T> +QStringHash<T>::QStringHash(const QStringHash<T> &other) +: newedNodes(0), nodePool(0), link(0) +{ + 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, iterateFirst(), iterateNext); + + 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] = 0; + Node *n = (Node *)other.data.buckets[ii]; + data.buckets[ii] = n; + } + + 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 = 0; + data.numBuckets = 0; + data.numBits = 0; + data.size = 0; + + newedNodes = 0; + nodePool = 0; + link = 0; +} + +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> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const QHashedString &key, const T &value) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + rv->length = key.length(); + rv->hash = key.hash(); + rv->strData = const_cast<QHashedString &>(key).data_ptr(); + rv->strData->ref.ref(); + rv->setQString(true); + rv->value = value; + return rv; + } else { + NewedNode *rv = new NewedNode(key, value); + rv->nextNewed = newedNodes; + newedNodes = rv; + return rv; + } +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const QHashedCStringRef &key, const T &value) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + rv->length = key.length(); + rv->hash = key.hash(); + rv->ckey = key.constData(); + rv->value = value; + return rv; + } else { + NewedNode *rv = new NewedNode(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>::copy(const QStringHash<T> &other) +{ + Q_ASSERT(data.size == 0); + + data.size = other.data.size; + + // Ensure buckets array is created + data.rehashToBits(data.numBits, iterateFirst(), iterateNext); + + if (other.link) { + for (ConstIterator iter = other.begin(); iter != other.end(); ++iter) { + Node *o = iter.node(); + Node *n = o->isQString()?findNode(QHashedStringRef((QChar *)o->strData->data(), o->length, o->hash)): + findNode(QHashedCStringRef(o->ckey, o->length, o->hash)); + if (!n) { + Node *mynode = takeNode(*o); + int bucket = mynode->hash % data.numBuckets; + mynode->next = data.buckets[bucket]; + data.buckets[bucket] = mynode; + } + } + } else { + for (ConstIterator iter = other.begin(); iter != other.end(); ++iter) { + Node *o = iter.node(); + Node *mynode = takeNode(*o); + int bucket = mynode->hash % data.numBuckets; + mynode->next = data.buckets[bucket]; + data.buckets[bucket] = mynode; + } + } +} + +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 = 0; + } else { + NewedNode *nn = (NewedNode *)node; + node = nn->nextNewed; + + if (node == 0 && This->nodePool && This->nodePool->used) + node = This->nodePool->nodes + This->nodePool->used - 1; + } + + if (node == 0 && 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 = 0; + if (newedNodes) + n = newedNodes; + else if (nodePool && nodePool->used) + n = nodePool->nodes + nodePool->used - 1; + + if (n == 0 && 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>::Node *QStringHash<T>::createNode(const Node &o) +{ + Node *n = takeNode(o); + + if (data.size >= data.numBuckets) + data.rehashToBits(data.numBits + 1, iterateFirst(), iterateNext, n); + + int bucket = n->hash % data.numBuckets; + n->next = data.buckets[bucket]; + data.buckets[bucket] = n; + + data.size++; + + return n; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const QHashedString &key, const T &value) +{ + Node *n = takeNode(key, value); + + if (data.size >= data.numBuckets) + data.rehashToBits(data.numBits + 1, iterateFirst(), iterateNext, n); + + int bucket = key.hash() % data.numBuckets; + n->next = data.buckets[bucket]; + data.buckets[bucket] = n; + + data.size++; + + return n; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const QHashedCStringRef &key, const T &value) +{ + Node *n = takeNode(key, value); + + if (data.size >= data.numBuckets) + data.rehashToBits(data.numBits + 1, iterateFirst(), iterateNext, n); + + int bucket = key.hash() % data.numBuckets; + n->next = data.buckets[bucket]; + data.buckets[bucket] = n; + + data.size++; + + return n; +} + +template<class T> +void QStringHash<T>::insert(const QString &key, const T &value) +{ + QHashedStringRef ch(key); + // If this is a linked hash, we can't rely on owning the node, so we always + // create a new one. + Node *n = link?0:findNode(key); + if (n) n->value = value; + else createNode(QHashedString(key, ch.hash()), value); +} + +template<class T> +void QStringHash<T>::insert(const QHashedString &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?0:findNode(key); + if (n) n->value = value; + else createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const QHashedStringRef &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?0:findNode(key); + if (n) n->value = value; + else createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const QHashedCStringRef &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?0:findNode(key); + if (n) n->value = value; + else createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const ConstIterator &key) +{ + // If this is a linked hash, we can't rely on owning the node, so we always + // create a new one. + if (key.node()->isQString()) { + QHashedStringRef str((QChar *)key.node()->strData->data(), key.node()->length, + key.node()->hash); + + Node *n = link?0:findNode(str); + if (n) n->value = key.node()->value; + else createNode(*key.node()); + } else { + QHashedCStringRef str(key.node()->ckey, key.node()->length, key.node()->hash); + + Node *n = link?0:findNode(str); + if (n) n->value = key.node()->value; + else createNode(str, key.node()->value); + } +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QString &string) const +{ + return findNode(QHashedStringRef(string)); +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QHashedString &string) const +{ + return findNode(QHashedStringRef(string.constData(), string.length(), string.hash())); +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QHashedStringRef &string) const +{ + QStringHashNode *node = data.numBuckets?data.buckets[string.hash() % data.numBuckets]:0; + while (node && !node->equals(string)) + node = (*node->next); + + return (Node *)node; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QHashedCStringRef &string) const +{ + QStringHashNode *node = data.numBuckets?data.buckets[string.hash() % data.numBuckets]:0; + while (node && !node->equals(string)) + node = (*node->next); + + return (Node *)node; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QHashedV8String &string) const +{ + QStringHashNode *node = data.numBuckets?data.buckets[string.hash() % data.numBuckets]:0; + while (node && !node->equals(string)) + node = (*node->next); + + return (Node *)node; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findSymbolNode(const QHashedV8String &string) const +{ + Q_ASSERT(string.symbolId() != 0); + + QStringHashNode *node = data.numBuckets?data.buckets[string.hash() % data.numBuckets]:0; + while (node && !node->symbolEquals(string)) + node = (*node->next); + + if (node) + node->symbolId = string.symbolId(); + + return (Node *)node; +} + +template<class T> +T *QStringHash<T>::value(const QString &key) const +{ + Node *n = findNode(key); + return n?&n->value:0; +} + +template<class T> +T *QStringHash<T>::value(const QHashedString &key) const +{ + Node *n = findNode(key); + return n?&n->value:0; +} + +template<class T> +T *QStringHash<T>::value(const QHashedStringRef &key) const +{ + Node *n = findNode(key); + return n?&n->value:0; +} + +template<class T> +T *QStringHash<T>::value(const QHashedCStringRef &key) const +{ + Node *n = findNode(key); + return n?&n->value:0; +} + +template<class T> +T *QStringHash<T>::value(const ConstIterator &iter) const +{ + Node *n = iter.node(); + if (n->isQString()) + return value(QHashedStringRef((QChar *)n->strData->data(), n->length, n->hash)); + else + return value(QHashedCStringRef(n->ckey, n->length, n->hash)); +} + +template<class T> +T *QStringHash<T>::value(const QHashedV8String &string) const +{ + Node *n = string.symbolId()?findSymbolNode(string):findNode(string); + return n?&n->value:0; +} + +template<class T> +bool QStringHash<T>::contains(const QString &s) const +{ + return 0 != value(s); +} + +template<class T> +bool QStringHash<T>::contains(const QHashedString &s) const +{ + return 0 != value(s); +} + +template<class T> +bool QStringHash<T>::contains(const QHashedStringRef &s) const +{ + return 0 != value(s); +} + +template<class T> +bool QStringHash<T>::contains(const QHashedCStringRef &s) const +{ + return 0 != value(s); +} + +template<class T> +bool QStringHash<T>::contains(const ConstIterator &s) const +{ + return 0 != value(s); +} + +template<class T> +T &QStringHash<T>::operator[](const QString &key) +{ + QHashedStringRef cs(key); + Node *n = findNode(cs); + if (n) return n->value; + else return createNode(QHashedString(key, cs.hash()), T())->value; +} + +template<class T> +T &QStringHash<T>::operator[](const QHashedString &key) +{ + Node *n = findNode(key); + if (n) return n->value; + else return createNode(key, T())->value; +} + +template<class T> +T &QStringHash<T>::operator[](const QHashedStringRef &key) +{ + Node *n = findNode(key); + if (n) return n->value; + else return createNode(key, T())->value; +} + +template<class T> +T &QStringHash<T>::operator[](const QHashedCStringRef &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, iterateFirst(), iterateNext); +} + +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> +QHashedString QStringHash<T>::ConstIterator::key() const +{ + Node *n = (Node *)d.n; + if (n->isQString()) { + return QHashedString(QString((QChar *)n->strData->data(), n->length), n->hash); + } else { + return QHashedString(QString::fromLatin1(n->ckey, n->length), n->hash); + } +} +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(); +} + +inline uint qHash(const QHashedString &string) +{ + return uint(string.hash()); +} + +inline uint qHash(const QHashedStringRef &string) +{ + return uint(string.hash()); +} + +QHashedString::QHashedString() +: QString(), m_hash(0) +{ +} + +QHashedString::QHashedString(const QString &string) +: QString(string), m_hash(0) +{ +} + +QHashedString::QHashedString(const QString &string, quint32 hash) +: QString(string), m_hash(hash) +{ +} + +QHashedString::QHashedString(const QHashedString &string) +: QString(string), m_hash(string.m_hash) +{ +} + +QHashedString &QHashedString::operator=(const QHashedString &string) +{ + static_cast<QString &>(*this) = string; + m_hash = string.m_hash; + return *this; +} + +bool QHashedString::operator==(const QHashedString &string) const +{ + return (string.m_hash == m_hash || !string.m_hash || !m_hash) && + static_cast<const QString &>(*this) == static_cast<const QString &>(string); +} + +bool QHashedString::operator==(const QHashedStringRef &string) const +{ + return length() == string.m_length && + (string.m_hash == m_hash || !string.m_hash || !m_hash) && + QHashedString::compare(constData(), string.m_data, string.m_length); +} + +quint32 QHashedString::hash() const +{ + if (!m_hash) computeHash(); + return m_hash; +} + +quint32 QHashedString::existingHash() const +{ + return m_hash; +} + +bool QHashedString::isUpper(const QChar &qc) +{ + ushort c = qc.unicode(); + // Optimize for _, a-z and A-Z. + return ((c != '_' ) && (!(c >= 'a' && c <= 'z')) && + ((c >= 'A' && c <= 'Z') || QChar::category(c) == QChar::Letter_Uppercase)); +} + +QHashedV8String::QHashedV8String() +{ +} + +QHashedV8String::QHashedV8String(v8::Handle<v8::String> string) +: m_hash(string->CompleteHash()), m_string(string) +{ + Q_ASSERT(!m_string.IsEmpty()); +} + +QHashedV8String::QHashedV8String(const QHashedV8String &string) +: m_hash(string.m_hash), m_string(string.m_string) +{ +} + +QHashedV8String &QHashedV8String::operator=(const QHashedV8String &other) +{ + m_hash = other.m_hash; + m_string = other.m_string; + return *this; +} + +bool QHashedV8String::operator==(const QHashedV8String &string) +{ + return m_hash.hash == string.m_hash.hash && m_hash.length == string.m_hash.length && + m_string.IsEmpty() == m_string.IsEmpty() && + (m_string.IsEmpty() || m_string->StrictEquals(string.m_string)); +} + +quint32 QHashedV8String::hash() const +{ + return m_hash.hash; +} + +int QHashedV8String::length() const +{ + return m_hash.length; +} + +quint32 QHashedV8String::symbolId() const +{ + return m_hash.symbol_id; +} + +v8::Handle<v8::String> QHashedV8String::string() const +{ + return m_string; +} + +QString QHashedV8String::toString() const +{ + QString result; + result.reserve(m_hash.length); + + for (int i = 0; i < m_hash.length; ++i) + result.append(m_string->GetCharacter(i)); + + return result; +} + +QHashedStringRef::QHashedStringRef() +: m_data(0), m_length(0), m_utf8length(-1), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QString &str) +: m_data(str.constData()), m_length(str.length()), m_utf8length(0), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QStringRef &str) +: m_data(str.constData()), m_length(str.length()), m_utf8length(0), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QChar *data, int length) +: m_data(data), m_length(length), m_utf8length(0), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QChar *data, int length, quint32 hash) +: m_data(data), m_length(length), m_utf8length(0), m_hash(hash) +{ +} + +QHashedStringRef::QHashedStringRef(const QHashedString &string) +: m_data(string.constData()), m_length(string.length()), m_utf8length(0), m_hash(string.m_hash) +{ +} + +QHashedStringRef::QHashedStringRef(const QHashedStringRef &string) +: m_data(string.m_data), m_length(string.m_length), m_utf8length(string.m_utf8length), + m_hash(string.m_hash) +{ +} + +QHashedStringRef &QHashedStringRef::operator=(const QHashedStringRef &o) +{ + m_data = o.m_data; + m_length = o.m_length; + m_utf8length = o.m_utf8length; + m_hash = o.m_hash; + return *this; +} + +bool QHashedStringRef::operator==(const QString &string) const +{ + return m_length == string.length() && + QHashedString::compare(string.constData(), m_data, m_length); +} + +bool QHashedStringRef::operator==(const QHashedString &string) const +{ + return m_length == string.length() && + (m_hash == string.m_hash || !m_hash || !string.m_hash) && + QHashedString::compare(string.constData(), m_data, m_length); +} + +bool QHashedStringRef::operator==(const QHashedStringRef &string) const +{ + return m_length == string.m_length && + (m_hash == string.m_hash || !m_hash || !string.m_hash) && + QHashedString::compare(string.m_data, m_data, m_length); +} + +bool QHashedStringRef::operator==(const QHashedCStringRef &string) const +{ + return m_length == string.m_length && + (m_hash == string.m_hash || !m_hash || !string.m_hash) && + QHashedString::compare(m_data, string.m_data, m_length); +} + +bool QHashedStringRef::operator!=(const QString &string) const +{ + return m_length != string.length() || + !QHashedString::compare(string.constData(), m_data, m_length); +} + +bool QHashedStringRef::operator!=(const QHashedString &string) const +{ + return m_length != string.length() || + (m_hash != string.m_hash && m_hash && string.m_hash) || + !QHashedString::compare(string.constData(), m_data, m_length); +} + +bool QHashedStringRef::operator!=(const QHashedStringRef &string) const +{ + return m_length != string.m_length || + (m_hash != string.m_hash && m_hash && string.m_hash) || + QHashedString::compare(string.m_data, m_data, m_length); +} + +bool QHashedStringRef::operator!=(const QHashedCStringRef &string) const +{ + return m_length != string.m_length || + (m_hash != string.m_hash && m_hash && string.m_hash) || + QHashedString::compare(m_data, string.m_data, m_length); +} + +const QChar &QHashedStringRef::at(int index) const +{ + Q_ASSERT(index < m_length); + return m_data[index]; +} + +const QChar *QHashedStringRef::constData() const +{ + return m_data; +} + +bool QHashedStringRef::isEmpty() const +{ + return m_length == 0; +} + +int QHashedStringRef::length() const +{ + return m_length; +} + +int QHashedStringRef::utf8length() const +{ + if (m_utf8length < m_length) + computeUtf8Length(); + return m_utf8length; +} + +bool QHashedStringRef::startsWithUpper() const +{ + if (m_length < 1) return false; + return QHashedString::isUpper(m_data[0]); +} + +quint32 QHashedStringRef::hash() const +{ + if (!m_hash) computeHash(); + return m_hash; +} + +QHashedCStringRef::QHashedCStringRef() +: m_data(0), m_length(0), m_hash(0) +{ +} + +QHashedCStringRef::QHashedCStringRef(const char *data, int length) +: m_data(data), m_length(length), m_hash(0) +{ +} + +QHashedCStringRef::QHashedCStringRef(const char *data, int length, quint32 hash) +: m_data(data), m_length(length), m_hash(hash) +{ +} + +QHashedCStringRef::QHashedCStringRef(const QHashedCStringRef &o) +: m_data(o.m_data), m_length(o.m_length), m_hash(o.m_hash) +{ +} + +quint32 QHashedCStringRef::hash() const +{ + if (!m_hash) computeHash(); + return m_hash; +} + +const char *QHashedCStringRef::constData() const +{ + return m_data; +} + +int QHashedCStringRef::length() const +{ + return m_length; +} + +int QHashedCStringRef::utf16length() const +{ + return m_length; +} + +void QHashedCStringRef::writeUtf16(QChar *output) const +{ + writeUtf16((uint16_t *)output); +} + +void QHashedCStringRef::writeUtf16(uint16_t *output) const +{ + int l = m_length; + const char *d = m_data; + while (l--) + *output++ = *d++; +} + +bool QHashedString::compare(const QChar *lhs, const char *rhs, int length) +{ + Q_ASSERT(lhs && rhs); + const quint16 *l = (const quint16*)lhs; + while (length--) + if (*l++ != *rhs++) return false; + return true; +} + +bool QHashedString::compare(const char *lhs, const char *rhs, int length) +{ + Q_ASSERT(lhs && rhs); + return 0 == ::memcmp(lhs, rhs, length); +} + +QT_END_NAMESPACE + +#endif // QHASHEDSTRING_P_H diff --git a/src/qml/qml/ftw/qhashfield_p.h b/src/qml/qml/ftw/qhashfield_p.h new file mode 100644 index 0000000000..46df9a176c --- /dev/null +++ b/src/qml/qml/ftw/qhashfield_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHASHFIELD_P_H +#define QHASHFIELD_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> + +QT_BEGIN_NAMESPACE + +// QHashField can be used for doing coarse grained set testing, in +// cases where you do not expect the set to contain the item. For +// example where you would write: +// QSet<QString> strings; +// for (int ii = 0; ii < mystrings.count(); ++ii) { +// if (strings.contains(mystrings.at(ii))) +// qFatal("Duplication!"); +// strings.insert(mystrings); +// } +// You may write: +// QHashField strings; +// for (int ii = 0; ii < mystrings.count(); ++ii) { +// if (strings.testAndSet(qHash(mystrings.at(ii)))) { +// // The string *might* be duplicated +// for (int jj = 0; jj < ii; ++jj) { +// if (mystrings.at(ii) == mystrings.at(jj)) +// qFatal("Duplication!"); +// } +// } +// } +// For small lists of things, where the hash is cheap to calculate +// and you don't expect duplication this will be much faster. +class QHashField { +public: + inline QHashField(); + + inline void clear(); + + inline bool test(quint32 hash); + inline bool testAndSet(quint32 hash); +private: + quint32 m_field; +}; + +QHashField::QHashField() +: m_field(0) +{ +} + +void QHashField::clear() +{ + m_field = 0; +} + +bool QHashField::test(quint32 hash) +{ + return m_field & (1 << (hash % 31)); +} + +bool QHashField::testAndSet(quint32 hash) +{ + quint32 mask = 1 << (hash % 31); + bool rv = m_field & mask; + m_field |= mask; + return rv; +} + +QT_END_NAMESPACE + +#endif // QHASHFIELD_P_H diff --git a/src/qml/qml/ftw/qintrusivelist.cpp b/src/qml/qml/ftw/qintrusivelist.cpp new file mode 100644 index 0000000000..5a1624f1f4 --- /dev/null +++ b/src/qml/qml/ftw/qintrusivelist.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qintrusivelist_p.h" + +/*! +\class QIntrusiveList +\brief The QIntrusiveList class is a template class that provides a list of objects using static storage. +\internal + +QIntrusiveList creates a linked list of objects. Adding and removing objects from the +QIntrusiveList is a constant time operation and is very quick. The list performs no memory +allocations, but does require the objects being added to the list to contain a QIntrusiveListNode +instance for the list's use. Even so, for small lists QIntrusiveList uses less memory than Qt's +other list classes. + +As QIntrusiveList uses storage inside the objects in the list, each object can only be in one +list at a time. Objects are inserted by the insert() method. If the object is already +in a list (including the one it is being inserted into) it is first removed, and then inserted +at the head of the list. QIntrusiveList is a last-in-first-out list. That is, following an +insert() the inserted object becomes the list's first() object. + +\code +struct MyObject { + MyObject(int value) : value(value) {} + + int value; + QIntrusiveListNode node; +}; +typedef QIntrusiveList<MyObject, &MyObject::node> MyObjectList; + +void foo() { + MyObjectList list; + + MyObject m0(0); + MyObject m1(1); + MyObject m2(2); + + list.insert(&m0); + list.insert(&m1); + list.insert(&m2); + + // QIntrusiveList is LIFO, so will print: 2... 1... 0... + for (MyObjectList::iterator iter = list.begin(); iter != list.end(); ++iter) { + qWarning() << iter->value; + } +} +\endcode +*/ + + +/*! +\fn QIntrusiveList::QIntrusiveList(); + +Construct an empty list. +*/ + +/*! +\fn QIntrusiveList::~QIntrusiveList(); + +Destroy the list. All entries are removed. +*/ + +/*! +\fn void QIntrusiveList::insert(N *object); + +Insert \a object into the list. If \a object is a member of this, or another list, it will be +removed and inserted at the head of this list. +*/ + +/*! +\fn void QIntrusiveList::remove(N *object); + +Remove \a object from the list. \a object must not be null. +*/ + +/*! +\fn bool QIntrusiveList::contains(N *object) const + +Returns true if the list contains \a object; otherwise returns false. +*/ + +/*! +\fn N *QIntrusiveList::first() const + +Returns the first entry in this list, or null if the list is empty. +*/ + +/*! +\fn N *QIntrusiveList::next(N *current) + +Returns the next object after \a current, or null if \a current is the last object. \a current cannot be null. +*/ + +/*! +\fn iterator QIntrusiveList::begin() + +Returns an STL-style interator pointing to the first item in the list. + +\sa end() +*/ + +/*! +\fn iterator QIntrusiveList::end() + +Returns an STL-style iterator pointing to the imaginary item after the last item in the list. + +\sa begin() +*/ + +/*! +iterator &QInplacelist::iterator::erase() + +Remove the current object from the list, and return an iterator to the next element. +*/ + + +/*! +\fn QIntrusiveListNode::QIntrusiveListNode() + +Create a QIntrusiveListNode. +*/ + +/*! +\fn QIntrusiveListNode::~QIntrusiveListNode() + +Destroy the QIntrusiveListNode. If the node is in a list, it is removed. +*/ + +/*! +\fn void QIntrusiveListNode::remove() + +If in a list, remove this node otherwise do nothing. +*/ + +/*! +\fn bool QIntrusiveListNode::isInList() const + +Returns true if this node is in a list, false otherwise. +*/ + diff --git a/src/qml/qml/ftw/qintrusivelist_p.h b/src/qml/qml/ftw/qintrusivelist_p.h new file mode 100644 index 0000000000..489b02d656 --- /dev/null +++ b/src/qml/qml/ftw/qintrusivelist_p.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QINTRUSIVELIST_P_H +#define QINTRUSIVELIST_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> + +QT_BEGIN_NAMESPACE + +class QIntrusiveListNode; +template<class N, QIntrusiveListNode N::*member> +class QIntrusiveList +{ +public: + inline QIntrusiveList(); + inline ~QIntrusiveList(); + + inline bool isEmpty() const; + inline void insert(N *n); + inline void remove(N *n); + inline bool contains(N *) const; + + class iterator { + public: + inline iterator(); + inline iterator(N *value); + + inline N *operator*() const; + inline N *operator->() const; + inline bool operator==(const iterator &other) const; + inline bool operator!=(const iterator &other) const; + inline iterator &operator++(); + + inline iterator &erase(); + + private: + N *_value; + }; + typedef iterator Iterator; + + inline N *first() const; + static inline N *next(N *current); + + inline iterator begin(); + inline iterator end(); + +private: + static inline N *nodeToN(QIntrusiveListNode *node); + + QIntrusiveListNode *__first; +}; + +class QIntrusiveListNode +{ +public: + inline QIntrusiveListNode(); + inline ~QIntrusiveListNode(); + + inline void remove(); + inline bool isInList() const; + + QIntrusiveListNode *_next; + QIntrusiveListNode**_prev; +}; + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::iterator::iterator() +: _value(0) +{ +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::iterator::iterator(N *value) +: _value(value) +{ +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::iterator::operator*() const +{ + return _value; +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::iterator::operator->() const +{ + return _value; +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::iterator::operator==(const iterator &other) const +{ + return other._value == _value; +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::iterator::operator!=(const iterator &other) const +{ + return other._value != _value; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::operator++() +{ + _value = QIntrusiveList<N, member>::next(_value); + return *this; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::erase() +{ + N *old = _value; + _value = QIntrusiveList<N, member>::next(_value); + (old->*member).remove(); + return *this; +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::QIntrusiveList() +: __first(0) +{ +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::~QIntrusiveList() +{ + while (__first) __first->remove(); +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::isEmpty() const +{ + return __first == 0; +} + +template<class N, QIntrusiveListNode N::*member> +void QIntrusiveList<N, member>::insert(N *n) +{ + QIntrusiveListNode *nnode = &(n->*member); + nnode->remove(); + + nnode->_next = __first; + if (nnode->_next) nnode->_next->_prev = &nnode->_next; + __first = nnode; + nnode->_prev = &__first; +} + +template<class N, QIntrusiveListNode N::*member> +void QIntrusiveList<N, member>::remove(N *n) +{ + QIntrusiveListNode *nnode = &(n->*member); + nnode->remove(); +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::contains(N *n) const +{ + QIntrusiveListNode *nnode = __first; + while (nnode) { + if (nodeToN(nnode) == n) + return true; + nnode = nnode->_next; + } + return false; +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::first() const +{ + return __first?nodeToN(__first):0; +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::next(N *current) +{ + QIntrusiveListNode *nextnode = (current->*member)._next; + N *nextstruct = nextnode?nodeToN(nextnode):0; + return nextstruct; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::begin() +{ + return __first?iterator(nodeToN(__first)):iterator(); +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::end() +{ + return iterator(); +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::nodeToN(QIntrusiveListNode *node) +{ + return (N *)((char *)node - ((char *)&(((N *)0)->*member) - (char *)0)); +} + +QIntrusiveListNode::QIntrusiveListNode() +: _next(0), _prev(0) +{ +} + +QIntrusiveListNode::~QIntrusiveListNode() +{ + remove(); +} + +void QIntrusiveListNode::remove() +{ + if (_prev) *_prev = _next; + if (_next) _next->_prev = _prev; + _prev = 0; + _next = 0; +} + +bool QIntrusiveListNode::isInList() const +{ + return _prev != 0; +} + +QT_END_NAMESPACE + +#endif // QINTRUSIVELIST_P_H diff --git a/src/qml/qml/ftw/qlazilyallocated_p.h b/src/qml/qml/ftw/qlazilyallocated_p.h new file mode 100644 index 0000000000..960d84d5e7 --- /dev/null +++ b/src/qml/qml/ftw/qlazilyallocated_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLAZILYALLOCATED_P_H +#define QLAZILYALLOCATED_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 <private/qflagpointer_p.h> + +QT_BEGIN_NAMESPACE + +template<typename T> +class QLazilyAllocated { +public: + inline QLazilyAllocated(); + inline ~QLazilyAllocated(); + + inline bool isAllocated() const; + + inline T *operator->() const; + + inline T &value(); + inline const T &value() const; + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); +private: + mutable QFlagPointer<T> d; +}; + +template<typename T> +QLazilyAllocated<T>::QLazilyAllocated() +{ +} + +template<typename T> +QLazilyAllocated<T>::~QLazilyAllocated() +{ + delete *d; +} + +template<typename T> +bool QLazilyAllocated<T>::isAllocated() const +{ + return !d.isNull(); +} + +template<typename T> +T &QLazilyAllocated<T>::value() +{ + if (d.isNull()) d = new T; + return *(*d); +} + +template<typename T> +const T &QLazilyAllocated<T>::value() const +{ + if (d.isNull()) d = new T; + return *(*d); +} + +template<typename T> +T *QLazilyAllocated<T>::operator->() const +{ + return *d; +} + +template<typename T> +bool QLazilyAllocated<T>::flag() const +{ + return d.flag(); +} + +template<typename T> +void QLazilyAllocated<T>::setFlag() +{ + d.setFlag(); +} + +template<typename T> +void QLazilyAllocated<T>::clearFlag() +{ + d.clearFlag(); +} + +template<typename T> +void QLazilyAllocated<T>::setFlagValue(bool v) +{ + d.setFlagValue(v); +} + +QT_END_NAMESPACE + +#endif // QLAZILYALLOCATED_P_H diff --git a/src/qml/qml/ftw/qpodvector_p.h b/src/qml/qml/ftw/qpodvector_p.h new file mode 100644 index 0000000000..c96692667a --- /dev/null +++ b/src/qml/qml/ftw/qpodvector_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPODVECTOR_P_H +#define QPODVECTOR_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 <QDebug> + +QT_BEGIN_NAMESPACE + +template<class T, int Increment=1024> +class QPODVector +{ +public: + QPODVector() + : m_count(0), m_capacity(0), m_data(0) {} + ~QPODVector() { if (m_data) ::free(m_data); } + + const T &at(int idx) const { + return m_data[idx]; + } + + T &operator[](int idx) { + return m_data[idx]; + } + + void clear() { + m_count = 0; + } + + void prepend(const T &v) { + insert(0, v); + } + + void append(const T &v) { + insert(m_count, v); + } + + void insert(int idx, const T &v) { + if (m_count == m_capacity) { + m_capacity += Increment; + m_data = (T *)realloc(m_data, m_capacity * sizeof(T)); + } + int moveCount = m_count - idx; + if (moveCount) + ::memmove(m_data + idx + 1, m_data + idx, moveCount * sizeof(T)); + m_count++; + m_data[idx] = v; + } + + void reserve(int count) { + if (count >= m_capacity) { + m_capacity = (count + (Increment-1)) & (0xFFFFFFFF - Increment + 1); + m_data = (T *)realloc(m_data, m_capacity * sizeof(T)); + } + } + + void insertBlank(int idx, int count) { + int newSize = m_count + count; + reserve(newSize); + int moveCount = m_count - idx; + if (moveCount) + ::memmove(m_data + idx + count, m_data + idx, + moveCount * sizeof(T)); + m_count = newSize; + } + + void remove(int idx, int count = 1) { + int moveCount = m_count - (idx + count); + if (moveCount) + ::memmove(m_data + idx, m_data + idx + count, + moveCount * sizeof(T)); + m_count -= count; + } + + void removeOne(const T &v) { + int idx = 0; + while (idx < m_count) { + if (m_data[idx] == v) { + remove(idx); + return; + } + ++idx; + } + } + + int find(const T &v) { + for (int idx = 0; idx < m_count; ++idx) + if (m_data[idx] == v) + return idx; + return -1; + } + + bool contains(const T &v) { + return find(v) != -1; + } + + int count() const { + return m_count; + } + + void copyAndClear(QPODVector<T,Increment> &other) { + if (other.m_data) ::free(other.m_data); + other.m_count = m_count; + other.m_capacity = m_capacity; + other.m_data = m_data; + m_count = 0; + m_capacity = 0; + m_data = 0; + } + + QPODVector<T,Increment> &operator<<(const T &v) { append(v); return *this; } +private: + QPODVector(const QPODVector &); + QPODVector &operator=(const QPODVector &); + int m_count; + int m_capacity; + T *m_data; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/ftw/qpointervaluepair_p.h b/src/qml/qml/ftw/qpointervaluepair_p.h new file mode 100644 index 0000000000..7b0caf49bc --- /dev/null +++ b/src/qml/qml/ftw/qpointervaluepair_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPOINTERVALUEPAIR_P_H +#define QPOINTERVALUEPAIR_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 <private/qflagpointer_p.h> + +QT_BEGIN_NAMESPACE + +// QPointerValuePair is intended to help reduce the memory consumption of a class. +// In the common case, QPointerValuePair behaves like a pointer. In this mode, it +// consumes the same memory as a regular pointer. +// Additionally, QPointerValuePair can store an arbitrary value type in *addition* +// to the pointer. In this case, it uses slightly more memory than the pointer and +// value type combined. +// Consequently, this class is most useful in cases where a pointer is always stored +// and a value type is rarely stored. +template<typename P, typename V> +class QPointerValuePair { +public: + inline QPointerValuePair(); + inline QPointerValuePair(P *); + inline ~QPointerValuePair(); + + inline bool isNull() const; + + inline bool flag() const; + inline void setFlag(); + inline void clearFlag(); + inline void setFlagValue(bool); + + inline QPointerValuePair<P, V> &operator=(P *); + + inline P *operator->() const; + inline P *operator*() const; + + inline bool hasValue() const; + inline V &value(); + inline const V *constValue() const; + +private: + struct Value { P *pointer; V value; }; + QBiPointer<P, Value> d; +}; + +template<typename P, typename V> +QPointerValuePair<P, V>::QPointerValuePair() +{ +} + +template<typename P, typename V> +QPointerValuePair<P, V>::QPointerValuePair(P *p) +: d(p) +{ +} + +template<typename P, typename V> +QPointerValuePair<P, V>::~QPointerValuePair() +{ + if (d.isT2()) delete d.asT2(); +} + +template<typename P, typename V> +bool QPointerValuePair<P, V>::isNull() const +{ + if (d.isT1()) return 0 == d.asT1(); + else return d.asT2()->pointer == 0; +} + +template<typename P, typename V> +bool QPointerValuePair<P, V>::flag() const +{ + return d.flag(); +} + +template<typename P, typename V> +void QPointerValuePair<P, V>::setFlag() +{ + d.setFlag(); +} + +template<typename P, typename V> +void QPointerValuePair<P, V>::clearFlag() +{ + d.clearFlag(); +} + +template<typename P, typename V> +void QPointerValuePair<P, V>::setFlagValue(bool v) +{ + d.setFlagValue(v); +} + +template<typename P, typename V> +QPointerValuePair<P, V> &QPointerValuePair<P, V>::operator=(P *o) +{ + if (d.isT1()) d = o; + else d.asT2()->pointer = o; + return *this; +} + +template<typename P, typename V> +P *QPointerValuePair<P, V>::operator->() const +{ + if (d.isT1()) return d.asT1(); + else return d.asT2()->pointer; +} + +template<typename P, typename V> +P *QPointerValuePair<P, V>::operator*() const +{ + if (d.isT1()) return d.asT1(); + else return d.asT2()->pointer; +} + +template<typename P, typename V> +bool QPointerValuePair<P, V>::hasValue() const +{ + return d.isT2(); +} + +template<typename P, typename V> +V &QPointerValuePair<P, V>::value() +{ + if (d.isT1()) { + P *p = d.asT1(); + Value *value = new Value; + value->pointer = p; + d = value; + } + + return d.asT2()->value; +} + +// Will return null if hasValue() == false +template<typename P, typename V> +const V *QPointerValuePair<P, V>::constValue() const +{ + if (d.isT2()) return &d.asT2()->value; + else return 0; +} + +QT_END_NAMESPACE + +#endif // QPOINTERVALUEPAIR_P_H diff --git a/src/qml/qml/ftw/qqmlpool.cpp b/src/qml/qml/ftw/qqmlpool.cpp new file mode 100644 index 0000000000..6fd11d4b1e --- /dev/null +++ b/src/qml/qml/ftw/qqmlpool.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpool_p.h" + +// #define POOL_DEBUG + +QT_BEGIN_NAMESPACE + +void QQmlPool::newpage() +{ +#ifdef POOL_DEBUG + qWarning("QQmlPool: Allocating page"); +#endif + + Page *page = (Page *)malloc(sizeof(Page)); + page->header.next = _page; + page->header.free = page->memory; + _page = page; +} + +void QQmlPool::clear() +{ +#ifdef POOL_DEBUG + int count = 0; +#endif + + Class *c = _classList; + while (c) { + Class *n = c->_next; + c->_destroy(c); +#ifdef POOL_DEBUG + ++count; +#endif + c = n; + } + +#ifdef POOL_DEBUG + qWarning("QQmlPool: Destroyed %d objects", count); +#endif + + Page *p = _page; + while (p) { + Page *n = p->header.next; + free(p); + p = n; + } + + _classList = 0; + _page = 0; +} + + +QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qqmlpool_p.h b/src/qml/qml/ftw/qqmlpool_p.h new file mode 100644 index 0000000000..e4fa03ce34 --- /dev/null +++ b/src/qml/qml/ftw/qqmlpool_p.h @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPOOL_P_H +#define QQMLPOOL_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 <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +// Exported for QtQuick1 +class Q_QML_EXPORT QQmlPool +{ +public: + // The class has a destructor that needs to be called + class Class { + public: + inline QQmlPool *pool() const; + + private: + void *operator new(size_t); + void *operator new(size_t, void *m) { return m; } + friend class QQmlPool; + + QQmlPool *_pool; + Class *_next; + void (*_destroy)(Class *); + }; + + // The class is plain old data and no destructor needs to + // be called + class POD { + public: + inline QQmlPool *pool() const; + + private: + void *operator new(size_t); + void *operator new(size_t, void *m) { return m; } + friend class QQmlPool; + + QQmlPool *_pool; + }; + + inline QQmlPool(); + inline ~QQmlPool(); + + void clear(); + + template<typename T> + inline T *New(); + template<typename T> + inline T *NewRaw(); + template<typename T> + inline T *NewRawArray(int length); + + inline QString *NewString(const QString &); + inline QByteArray *NewByteArray(const QByteArray &); + inline QUrl *NewUrl(const QUrl &); + + template<typename T> + struct List { + List() : m_length(0), m_data(0) {} + List(const List &o) : m_length(o.m_length), m_data(o.m_data) {} + List &operator=(const List &o) { + m_length = o.m_length; + m_data = o.m_data; + return *this; + } + + int count() const { + return m_length; + } + int length() const { + return m_length; + } + const T &at(int index) const { + Q_ASSERT(index < m_length); + return m_data[index]; + }; + T &operator[](int index) { + Q_ASSERT(index < m_length); + return m_data[index]; + }; + private: + friend class QQmlPool; + List(T *d, int l) : m_length(l), m_data(d) {} + int m_length; + T *m_data; + }; + + template<typename T> + inline List<T> NewRawList(int length); + +private: + struct StringClass : public QString, public Class { + }; + struct ByteArrayClass : public QByteArray, public Class { + }; + struct UrlClass : public QUrl, public Class { + }; + + inline void *allocate(int size); + void newpage(); + + template<typename T> + inline void initialize(POD *); + template<typename T> + inline void initialize(Class *); + template<typename T> + static void destroy(Class *c); + + struct Page { + struct Header { + Page *next; + char *free; + } header; + + static const int pageSize = 4 * 4096 - sizeof(Header); + + char memory[pageSize]; + }; + + Page *_page; + Class *_classList; +}; + +QQmlPool::QQmlPool() +: _page(0), _classList(0) +{ +} + +QQmlPool::~QQmlPool() +{ + clear(); +} + +template<typename T> +T *QQmlPool::New() +{ + T *rv = new (allocate(sizeof(T))) T; + initialize<T>(rv); + rv->_pool = this; + return rv; +} + +template<typename T> +T *QQmlPool::NewRaw() +{ + return (T*)allocate(sizeof(T)); +} + +template<typename T> +T *QQmlPool::NewRawArray(int length) +{ + return (T*)allocate(length * sizeof(T)); +} + +template<typename T> +QQmlPool::List<T> QQmlPool::NewRawList(int length) +{ + return List<T>(NewRawArray<T>(length), length); +} + +QString *QQmlPool::NewString(const QString &s) +{ + QString *rv = New<StringClass>(); + *rv = s; + return rv; +} + +QByteArray *QQmlPool::NewByteArray(const QByteArray &s) +{ + QByteArray *rv = New<ByteArrayClass>(); + *rv = s; + return rv; +} + +QUrl *QQmlPool::NewUrl(const QUrl &s) +{ + QUrl *rv = New<UrlClass>(); + *rv = s; + return rv; +} + +void *QQmlPool::allocate(int size) +{ + if (!_page || (_page->header.free + size) > (_page->memory + Page::pageSize)) + newpage(); + + void *rv = _page->header.free; + _page->header.free += size + ((8 - size) & 7); // ensure 8 byte alignment; + return rv; +} + +template<typename T> +void QQmlPool::initialize(QQmlPool::POD *) +{ +} + +template<typename T> +void QQmlPool::initialize(QQmlPool::Class *c) +{ + c->_next = _classList; + c->_destroy = &destroy<T>; + _classList = c; +} + +template<typename T> +void QQmlPool::destroy(Class *c) +{ + static_cast<T *>(c)->~T(); +} + +QQmlPool *QQmlPool::Class::pool() const +{ + return _pool; +} + +QQmlPool *QQmlPool::POD::pool() const +{ + return _pool; +} + +QT_END_NAMESPACE + +#endif // QQMLPOOL_P_H + diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h new file mode 100644 index 0000000000..497f4ecc0f --- /dev/null +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLREFCOUNT_P_H +#define QQMLREFCOUNT_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/qatomic.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlRefCount +{ +public: + inline QQmlRefCount(); + inline virtual ~QQmlRefCount(); + inline void addref(); + inline void release(); + +protected: + inline virtual void destroy(); + +private: + QAtomicInt refCount; +}; + +template<class T> +class QQmlRefPointer +{ +public: + inline QQmlRefPointer(); + inline QQmlRefPointer(T *); + inline QQmlRefPointer(const QQmlRefPointer<T> &); + inline ~QQmlRefPointer(); + + inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o); + inline QQmlRefPointer<T> &operator=(T *); + + inline bool isNull() const { return !o; } + + inline T* operator->() const { return o; } + inline T& operator*() const { return *o; } + inline operator T*() const { return o; } + inline T* data() const { return o; } + + inline QQmlRefPointer<T> &take(T *); + +private: + T *o; +}; + +QQmlRefCount::QQmlRefCount() +: refCount(1) +{ +} + +QQmlRefCount::~QQmlRefCount() +{ + Q_ASSERT(refCount.load() == 0); +} + +void QQmlRefCount::addref() +{ + Q_ASSERT(refCount.load() > 0); + refCount.ref(); +} + +void QQmlRefCount::release() +{ + Q_ASSERT(refCount.load() > 0); + if (!refCount.deref()) + destroy(); +} + +void QQmlRefCount::destroy() +{ + delete this; +} + +template<class T> +QQmlRefPointer<T>::QQmlRefPointer() +: o(0) +{ +} + +template<class T> +QQmlRefPointer<T>::QQmlRefPointer(T *o) +: o(o) +{ + if (o) o->addref(); +} + +template<class T> +QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other) +: o(other.o) +{ + if (o) o->addref(); +} + +template<class T> +QQmlRefPointer<T>::~QQmlRefPointer() +{ + if (o) o->release(); +} + +template<class T> +QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other) +{ + if (other.o) other.o->addref(); + if (o) o->release(); + o = other.o; + return *this; +} + +template<class T> +QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(T *other) +{ + if (other) other->addref(); + if (o) o->release(); + o = other; + return *this; +} + +/*! +Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership +of the callers reference of other. +*/ +template<class T> +QQmlRefPointer<T> &QQmlRefPointer<T>::take(T *other) +{ + if (o) o->release(); + o = other; + return *this; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLREFCOUNT_P_H diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp new file mode 100644 index 0000000000..423012b934 --- /dev/null +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlthread_p.h" + +#include <private/qfieldlist_p.h> + +#include <QtCore/qmutex.h> +#include <QtCore/qthread.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qcoreapplication.h> + +QT_BEGIN_NAMESPACE + +class QQmlThreadPrivate : public QThread +{ +public: + QQmlThreadPrivate(QQmlThread *); + QQmlThread *q; + + virtual void run(); + + inline void lock() { _mutex.lock(); } + inline void unlock() { _mutex.unlock(); } + inline void wait() { _wait.wait(&_mutex); } + inline void wakeOne() { _wait.wakeOne(); } + inline void wakeAll() { _wait.wakeAll(); } + + quint32 m_threadProcessing:1; // Set when the thread is processing messages + quint32 m_mainProcessing:1; // Set when the main thread is processing messages + quint32 m_shutdown:1; // Set by main thread to request a shutdown + quint32 m_mainThreadWaiting:1; // Set by main thread if it is waiting for the message queue to empty + + typedef QFieldList<QQmlThread::Message, &QQmlThread::Message::next> MessageList; + MessageList threadList; + MessageList mainList; + + QQmlThread::Message *mainSync; + + void triggerMainEvent(); + void triggerThreadEvent(); + + void mainEvent(); + void threadEvent(); + +protected: + virtual bool event(QEvent *); + +private: + struct MainObject : public QObject { + MainObject(QQmlThreadPrivate *p); + virtual bool event(QEvent *e); + QQmlThreadPrivate *p; + }; + MainObject m_mainObject; + + QMutex _mutex; + QWaitCondition _wait; +}; + +QQmlThreadPrivate::MainObject::MainObject(QQmlThreadPrivate *p) +: p(p) +{ +} + +// Trigger mainEvent in main thread. Must be called from thread. +void QQmlThreadPrivate::triggerMainEvent() +{ + Q_ASSERT(q->isThisThread()); + QCoreApplication::postEvent(&m_mainObject, new QEvent(QEvent::User)); +} + +// Trigger even in thread. Must be called from main thread. +void QQmlThreadPrivate::triggerThreadEvent() +{ + Q_ASSERT(!q->isThisThread()); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QQmlThreadPrivate::MainObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) + p->mainEvent(); + return QObject::event(e); +} + +QQmlThreadPrivate::QQmlThreadPrivate(QQmlThread *q) +: q(q), m_threadProcessing(false), m_mainProcessing(false), m_shutdown(false), + m_mainThreadWaiting(false), mainSync(0), m_mainObject(this) +{ +} + +bool QQmlThreadPrivate::event(QEvent *e) +{ + if (e->type() == QEvent::User) + threadEvent(); + return QThread::event(e); +} + +void QQmlThreadPrivate::run() +{ + lock(); + + wakeOne(); + + unlock(); + + q->startupThread(); + exec(); +} + +void QQmlThreadPrivate::mainEvent() +{ + lock(); + + m_mainProcessing = true; + + while (!mainList.isEmpty() || mainSync) { + bool isSync = mainSync != 0; + QQmlThread::Message *message = isSync?mainSync:mainList.takeFirst(); + unlock(); + + message->call(q); + delete message; + + lock(); + + if (isSync) { + mainSync = 0; + wakeOne(); + } + } + + m_mainProcessing = false; + + unlock(); +} + +void QQmlThreadPrivate::threadEvent() +{ + lock(); + + if (m_shutdown) { + quit(); + wakeOne(); + unlock(); + q->shutdownThread(); + } else { + m_threadProcessing = true; + + while (!threadList.isEmpty()) { + QQmlThread::Message *message = threadList.first(); + + unlock(); + + message->call(q); + + lock(); + + delete threadList.takeFirst(); + } + + wakeOne(); + + m_threadProcessing = false; + + unlock(); + } +} + +QQmlThread::QQmlThread() +: d(new QQmlThreadPrivate(this)) +{ + d->lock(); + d->start(); + d->wait(); + d->unlock(); + d->moveToThread(d); + +} + +QQmlThread::~QQmlThread() +{ + delete d; +} + +void QQmlThread::shutdown() +{ + d->lock(); + Q_ASSERT(!d->m_shutdown); + d->m_shutdown = true; + if (d->threadList.isEmpty() && d->m_threadProcessing == false) + d->triggerThreadEvent(); + d->wait(); + d->unlock(); + d->QThread::wait(); +} + +void QQmlThread::lock() +{ + d->lock(); +} + +void QQmlThread::unlock() +{ + d->unlock(); +} + +void QQmlThread::wakeOne() +{ + d->wakeOne(); +} + +void QQmlThread::wakeAll() +{ + d->wakeAll(); +} + +void QQmlThread::wait() +{ + d->wait(); +} + +bool QQmlThread::isThisThread() const +{ + return QThread::currentThread() == d; +} + +QThread *QQmlThread::thread() const +{ + return const_cast<QThread *>(static_cast<const QThread *>(d)); +} + +// Called when the thread starts. Do startup stuff in here. +void QQmlThread::startupThread() +{ +} + +// Called when the thread shuts down. Do cleanup in here. +void QQmlThread::shutdownThread() +{ +} + +void QQmlThread::internalCallMethodInThread(Message *message) +{ + Q_ASSERT(!isThisThread()); + d->lock(); + Q_ASSERT(d->m_mainThreadWaiting == false); + + bool wasEmpty = d->threadList.isEmpty(); + d->threadList.append(message); + if (wasEmpty && d->m_threadProcessing == false) + d->triggerThreadEvent(); + + d->m_mainThreadWaiting = true; + + do { + if (d->mainSync) { + QQmlThread::Message *message = d->mainSync; + unlock(); + message->call(this); + delete message; + lock(); + d->mainSync = 0; + wakeOne(); + } else { + d->wait(); + } + } while (d->mainSync || !d->threadList.isEmpty()); + + d->m_mainThreadWaiting = false; + d->unlock(); +} + +void QQmlThread::internalCallMethodInMain(Message *message) +{ + Q_ASSERT(isThisThread()); + + d->lock(); + + Q_ASSERT(d->mainSync == 0); + d->mainSync = message; + + if (d->m_mainThreadWaiting) { + d->wakeOne(); + } else if (d->m_mainProcessing) { + // Do nothing - it is already looping + } else { + d->triggerMainEvent(); + } + + while (d->mainSync && !d->m_shutdown) + d->wait(); + + d->unlock(); +} + +void QQmlThread::internalPostMethodToThread(Message *message) +{ + Q_ASSERT(!isThisThread()); + d->lock(); + bool wasEmpty = d->threadList.isEmpty(); + d->threadList.append(message); + if (wasEmpty && d->m_threadProcessing == false) + d->triggerThreadEvent(); + d->unlock(); +} + +void QQmlThread::internalPostMethodToMain(Message *message) +{ + Q_ASSERT(isThisThread()); + d->lock(); + bool wasEmpty = d->mainList.isEmpty(); + d->mainList.append(message); + if (wasEmpty && d->m_mainProcessing == false) + d->triggerMainEvent(); + d->unlock(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h new file mode 100644 index 0000000000..8a0ec6ceaa --- /dev/null +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTHREAD_P_H +#define QQMLTHREAD_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 <private/qintrusivelist_p.h> + +QT_BEGIN_NAMESPACE + +class QThread; + +class QQmlThreadPrivate; +class QQmlThread +{ +public: + QQmlThread(); + virtual ~QQmlThread(); + void shutdown(); + + void lock(); + void unlock(); + void wakeOne(); + void wakeAll(); + void wait(); + + QThread *thread() const; + bool isThisThread() const; + + // Synchronously invoke a method in the thread + template<class O> + inline void callMethodInThread(void (O::*Member)()); + template<typename T, class V, class O> + inline void callMethodInThread(void (O::*Member)(V), const T &); + template<typename T, typename T2, class V, class V2, class O> + inline void callMethodInThread(void (O::*Member)(V, V2), const T &, const T2 &); + + // Synchronously invoke a method in the main thread. If the main thread is + // blocked in a callMethodInThread() call, the call is made from within that + // call. + template<class O> + inline void callMethodInMain(void (O::*Member)()); + template<typename T, class V, class O> + inline void callMethodInMain(void (O::*Member)(V), const T &); + template<typename T, typename T2, class V, class V2, class O> + inline void callMethodInMain(void (O::*Member)(V, V2), const T &, const T2 &); + + // Asynchronously invoke a method in the thread. + template<class O> + inline void postMethodToThread(void (O::*Member)()); + template<typename T, class V, class O> + inline void postMethodToThread(void (O::*Member)(V), const T &); + template<typename T, typename T2, class V, class V2, class O> + inline void postMethodToThread(void (O::*Member)(V, V2), const T &, const T2 &); + + // Asynchronously invoke a method in the main thread. + template<class O> + inline void postMethodToMain(void (O::*Member)()); + template<typename T, class V, class O> + inline void postMethodToMain(void (O::*Member)(V), const T &); + template<typename T, typename T2, class V, class V2, class O> + inline void postMethodToMain(void (O::*Member)(V, V2), const T &, const T2 &); + +protected: + virtual void startupThread(); + virtual void shutdownThread(); + +private: + friend class QQmlThreadPrivate; + + struct Message { + Message() : next(0) {} + virtual ~Message() {} + Message *next; + virtual void call(QQmlThread *) = 0; + }; + void internalCallMethodInThread(Message *); + void internalCallMethodInMain(Message *); + void internalPostMethodToThread(Message *); + void internalPostMethodToMain(Message *); + QQmlThreadPrivate *d; +}; + +template<class O> +void QQmlThread::callMethodInThread(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(); + } + }; + internalCallMethodInThread(new I(Member)); +} + +template<typename T, class V, class O> +void QQmlThread::callMethodInThread(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg); + } + }; + internalCallMethodInThread(new I(Member, arg)); +} + +template<typename T, typename T2, class V, class V2, class O> +void QQmlThread::callMethodInThread(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg, arg2); + } + }; + internalCallMethodInThread(new I(Member, arg, arg2)); +} + +template<class O> +void QQmlThread::callMethodInMain(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(); + } + }; + internalCallMethodInMain(new I(Member)); +} + +template<typename T, class V, class O> +void QQmlThread::callMethodInMain(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg); + } + }; + internalCallMethodInMain(new I(Member, arg)); +} + +template<typename T, typename T2, class V, class V2, class O> +void QQmlThread::callMethodInMain(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg, arg2); + } + }; + internalCallMethodInMain(new I(Member, arg, arg2)); +} + +template<class O> +void QQmlThread::postMethodToThread(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(); + } + }; + internalPostMethodToThread(new I(Member)); +} + +template<typename T, class V, class O> +void QQmlThread::postMethodToThread(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg); + } + }; + internalPostMethodToThread(new I(Member, arg)); +} + +template<typename T, typename T2, class V, class V2, class O> +void QQmlThread::postMethodToThread(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg, arg2); + } + }; + internalPostMethodToThread(new I(Member, arg, arg2)); +} + +template<class O> +void QQmlThread::postMethodToMain(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(); + } + }; + internalPostMethodToMain(new I(Member)); +} + +template<typename T, class V, class O> +void QQmlThread::postMethodToMain(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg); + } + }; + internalPostMethodToMain(new I(Member, arg)); +} + +template<typename T, typename T2, class V, class V2, class O> +void QQmlThread::postMethodToMain(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QQmlThread *thread) { + O *me = static_cast<O *>(thread); + (me->*Member)(arg, arg2); + } + }; + internalPostMethodToMain(new I(Member, arg, arg2)); +} + +QT_END_NAMESPACE + +#endif // QQMLTHREAD_P_H diff --git a/src/qml/qml/ftw/qqmltrace.cpp b/src/qml/qml/ftw/qqmltrace.cpp new file mode 100644 index 0000000000..e044dc654f --- /dev/null +++ b/src/qml/qml/ftw/qqmltrace.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltrace_p.h" + +#ifdef QML_ENABLE_TRACE +#include <stdio.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef QML_ENABLE_TRACE + +QQmlTrace::Pool QQmlTrace::logPool; +QQmlTrace::Entry *QQmlTrace::first = 0; +QQmlTrace::Entry *QQmlTrace::last = 0; + +static qint64 toNsecs(QQmlTrace::TimeType time) +{ +#ifdef Q_OS_MAC + static mach_timebase_info_data_t info = {0,0}; + if (info.denom == 0) + mach_timebase_info(&info); + return time * info.numer / info.denom; +#else + qint64 rv = time.tv_sec * 1000000000 + time.tv_nsec; + return rv; +#endif +} + +QQmlTrace::Pool::Pool() +{ + first = New<Entry>(); + last = first; +} + +QQmlTrace::Pool::~Pool() +{ + char buffer[128]; + sprintf(buffer, "qml.%d.log", ::getpid()); + FILE *out = fopen(buffer, "w"); + if (!out) { + fprintf (stderr, "QML Log: Could not open %s\n", buffer); + return; + } else { + fprintf (stderr, "QML Log: Writing log to %s\n", buffer); + } + + QQmlTrace::Entry *cur = QQmlTrace::first; + QByteArray indent; + int depth = -1; + + qint64 firstTime = -1; + + while (cur) { + + switch (cur->type) { + case QQmlTrace::Entry::RangeStart: { + RangeStart *rs = static_cast<QQmlTrace::RangeStart *>(cur); + + qint64 nsecs = toNsecs(rs->time); + + if (firstTime == -1) + firstTime = nsecs; + + nsecs -= firstTime; + + depth++; + indent = QByteArray(depth * 4, ' '); + fprintf(out, "%s%s @%lld (%lld ns)\n", indent.constData(), + rs->description, nsecs, toNsecs(rs->end->time) - nsecs - firstTime); + } break; + case QQmlTrace::Entry::RangeEnd: + depth--; + indent = QByteArray(depth * 4, ' '); + break; + case QQmlTrace::Entry::Detail: + fprintf(out, "%s %s\n", indent.constData(), + static_cast<QQmlTrace::Detail *>(cur)->description); + break; + case QQmlTrace::Entry::IntDetail: + fprintf(out, "%s %s: %d\n", indent.constData(), + static_cast<QQmlTrace::Detail *>(cur)->description, + static_cast<QQmlTrace::IntDetail *>(cur)->value); + break; + case QQmlTrace::Entry::StringDetail: { + QByteArray vLatin1 = static_cast<QQmlTrace::StringDetail *>(cur)->value->toLatin1(); + fprintf(out, "%s %s: %s\n", indent.constData(), + static_cast<QQmlTrace::Detail *>(cur)->description, + vLatin1.constData()); + } break; + case QQmlTrace::Entry::UrlDetail: { + QByteArray vLatin1 = static_cast<QQmlTrace::UrlDetail *>(cur)->value->toString().toLatin1(); + fprintf(out, "%s %s: %s\n", indent.constData(), + static_cast<QQmlTrace::Detail *>(cur)->description, + vLatin1.constData()); + } break; + case QQmlTrace::Entry::Event: { + Event *ev = static_cast<QQmlTrace::Event *>(cur); + qint64 nsecs = toNsecs(ev->time) - firstTime; + fprintf(out, "%s + %s @%lld +%lld ns\n", indent.constData(), + ev->description, nsecs, nsecs - (toNsecs(ev->start->time) - firstTime)); + } break; + case QQmlTrace::Entry::Null: + default: + break; + } + cur = cur->next; + } + fclose(out); +} + +#endif + +QT_END_NAMESPACE + diff --git a/src/qml/qml/ftw/qqmltrace_p.h b/src/qml/qml/ftw/qqmltrace_p.h new file mode 100644 index 0000000000..965baff3a3 --- /dev/null +++ b/src/qml/qml/ftw/qqmltrace_p.h @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTRACE_P_H +#define QQMLTRACE_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 <private/qqmlpool_p.h> + +// #define QML_ENABLE_TRACE + +#if defined(QML_ENABLE_TRACE) && defined(Q_OS_MAC) +#include <mach/mach_time.h> +#endif + +QT_BEGIN_NAMESPACE + +class QUrl; +class QQmlTrace +{ +public: + inline QQmlTrace(const char *desc); + inline ~QQmlTrace(); + + inline void addDetail(const char *); + inline void addDetail(const char *, int); + inline void addDetail(const char *, const QString &); + inline void addDetail(const char *, const QUrl &); + + inline void event(const char *desc); + +#ifdef QML_ENABLE_TRACE + +#ifdef Q_OS_MAC + typedef uint64_t TimeType; +#else + typedef timespec TimeType; +#endif + + struct Entry : public QQmlPool::POD { + enum Type { Null, RangeStart, RangeEnd, Detail, IntDetail, StringDetail, UrlDetail, Event }; + inline Entry(); + inline Entry(Type); + Type type; + Entry *next; + }; + struct RangeEnd : public Entry { + inline RangeEnd(); + TimeType time; + }; + struct RangeStart : public Entry { + inline RangeStart(); + const char *description; + TimeType time; + QQmlTrace::RangeEnd *end; + }; + struct Detail : public Entry { + inline Detail(); + inline Detail(Type t); + const char *description; + }; + struct IntDetail : public Detail { + inline IntDetail(); + int value; + }; + struct StringDetail : public Detail { + inline StringDetail(); + QString *value; + }; + struct UrlDetail : public Detail { + inline UrlDetail(); + QUrl *value; + }; + struct Event : public Entry { + inline Event(); + const char *description; + TimeType time; + QQmlTrace::RangeStart *start; + }; + + struct Pool : public QQmlPool { + Pool(); + ~Pool(); + }; + + static Pool logPool; + static Entry *first; + static Entry *last; + +private: + RangeStart *start; + + static TimeType gettime() { +#ifdef Q_OS_MAC + return mach_absolute_time(); +#else + TimeType ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts; +#endif + } +#endif +}; + +#ifdef QML_ENABLE_TRACE +QQmlTrace::Entry::Entry() +: type(Null), next(0) +{ +} + +QQmlTrace::Entry::Entry(Type type) +: type(type), next(0) +{ + QQmlTrace::last->next = this; + QQmlTrace::last = this; +} + +QQmlTrace::RangeEnd::RangeEnd() +: QQmlTrace::Entry(QQmlTrace::Entry::RangeEnd), + time(gettime()) +{ +} + +QQmlTrace::RangeStart::RangeStart() +: QQmlTrace::Entry(QQmlTrace::Entry::RangeStart), + description(0), time(gettime()) +{ +} + +QQmlTrace::Detail::Detail() +: QQmlTrace::Entry(QQmlTrace::Entry::Detail), + description(0) +{ +} + +QQmlTrace::Detail::Detail(Type type) +: QQmlTrace::Entry(type), description(0) +{ +} + +QQmlTrace::IntDetail::IntDetail() +: QQmlTrace::Detail(QQmlTrace::Entry::IntDetail), + value(0) +{ +} + +QQmlTrace::StringDetail::StringDetail() +: QQmlTrace::Detail(QQmlTrace::Entry::StringDetail), + value(0) +{ +} + +QQmlTrace::UrlDetail::UrlDetail() +: QQmlTrace::Detail(QQmlTrace::Entry::UrlDetail), + value(0) +{ +} + +QQmlTrace::Event::Event() +: QQmlTrace::Entry(QQmlTrace::Entry::Event), + description(0), time(gettime()), start(0) +{ +} +#endif + +QQmlTrace::QQmlTrace(const char *desc) +{ +#ifdef QML_ENABLE_TRACE + RangeStart *e = logPool.New<RangeStart>(); + e->description = desc; + e->end = 0; + start = e; +#else + Q_UNUSED(desc); +#endif +} + +QQmlTrace::~QQmlTrace() +{ +#ifdef QML_ENABLE_TRACE + RangeEnd *e = logPool.New<RangeEnd>(); + start->end = e; +#endif +} + +void QQmlTrace::addDetail(const char *desc) +{ +#ifdef QML_ENABLE_TRACE + Detail *e = logPool.New<Detail>(); + e->description = desc; +#else + Q_UNUSED(desc); +#endif +} + +void QQmlTrace::addDetail(const char *desc, int v) +{ +#ifdef QML_ENABLE_TRACE + IntDetail *e = logPool.New<IntDetail>(); + e->description = desc; + e->value = v; +#else + Q_UNUSED(desc); + Q_UNUSED(v); +#endif +} + +void QQmlTrace::addDetail(const char *desc, const QString &v) +{ +#ifdef QML_ENABLE_TRACE + StringDetail *e = logPool.New<StringDetail>(); + e->description = desc; + e->value = logPool.NewString(v); +#else + Q_UNUSED(desc); + Q_UNUSED(v); +#endif +} + +void QQmlTrace::addDetail(const char *desc, const QUrl &v) +{ +#ifdef QML_ENABLE_TRACE + UrlDetail *e = logPool.New<UrlDetail>(); + e->description = desc; + e->value = logPool.NewUrl(v); +#else + Q_UNUSED(desc); + Q_UNUSED(v); +#endif +} + +void QQmlTrace::event(const char *desc) +{ +#ifdef QML_ENABLE_TRACE + Event *e = logPool.New<Event>(); + e->start = start; + e->description = desc; +#else + Q_UNUSED(desc); +#endif +} + +QT_END_NAMESPACE + +#endif // QQMLTRACE_P_H diff --git a/src/qml/qml/ftw/qrecursionwatcher_p.h b/src/qml/qml/ftw/qrecursionwatcher_p.h new file mode 100644 index 0000000000..16886edf12 --- /dev/null +++ b/src/qml/qml/ftw/qrecursionwatcher_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRECURSIONWATCHER_P_H +#define QRECURSIONWATCHER_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> + +QT_BEGIN_NAMESPACE + +class QRecursionNode; +class QRecursionNode { +public: + inline QRecursionNode(); + bool *_r; +}; + +template<class T, QRecursionNode T::*Node> +class QRecursionWatcher { +public: + inline QRecursionWatcher(T *); + inline ~QRecursionWatcher(); + inline bool hasRecursed() const; +private: + T *_t; + bool _r; +}; + +QRecursionNode::QRecursionNode() +: _r(0) +{ +} + +template<class T, QRecursionNode T::*Node> +QRecursionWatcher<T, Node>::QRecursionWatcher(T *t) +: _t(t), _r(false) +{ + if ((_t->*Node)._r) *(_t->*Node)._r = true; + (_t->*Node)._r = &_r; +} + +template<class T, QRecursionNode T::*Node> +QRecursionWatcher<T, Node>::~QRecursionWatcher() +{ + if ((_t->*Node)._r == &_r) (_t->*Node)._r = 0; +} + +template<class T, QRecursionNode T::*Node> +bool QRecursionWatcher<T, Node>::hasRecursed() const +{ + return _r; +} + +QT_END_NAMESPACE + +#endif // QRECURSIONWATCHER_P_H diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h new file mode 100644 index 0000000000..8d0f060ab3 --- /dev/null +++ b/src/qml/qml/ftw/qrecyclepool_p.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRECYCLEPOOL_P_H +#define QRECYCLEPOOL_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. +// + +QT_BEGIN_NAMESPACE + +#define QRECYCLEPOOLCOOKIE 0x33218ADF + +template<typename T, int Step> +class QRecyclePoolPrivate +{ +public: + QRecyclePoolPrivate() + : recyclePoolHold(true), outstandingItems(0), cookie(QRECYCLEPOOLCOOKIE), + currentPage(0), nextAllocated(0) + { + } + + bool recyclePoolHold; + int outstandingItems; + quint32 cookie; + + struct PoolType : public T { + union { + QRecyclePoolPrivate<T, Step> *pool; + PoolType *nextAllocated; + }; + }; + + struct Page { + Page *nextPage; + unsigned int free; + union { + char array[Step * sizeof(PoolType)]; + qint64 q_for_alignment_1; + double q_for_alignment_2; + }; + }; + + Page *currentPage; + PoolType *nextAllocated; + + inline T *allocate(); + static inline void dispose(T *); + inline void releaseIfPossible(); +}; + +template<typename T, int Step = 1024> +class QRecyclePool +{ +public: + inline QRecyclePool(); + inline ~QRecyclePool(); + + inline T *New(); + template<typename T1> + inline T *New(const T1 &); + template<typename T1> + inline T *New(T1 &); + + static inline void Delete(T *); + +private: + QRecyclePoolPrivate<T, Step> *d; +}; + +template<typename T, int Step> +QRecyclePool<T, Step>::QRecyclePool() +: d(new QRecyclePoolPrivate<T, Step>()) +{ +} + +template<typename T, int Step> +QRecyclePool<T, Step>::~QRecyclePool() +{ + d->recyclePoolHold = false; + d->releaseIfPossible(); +} + +template<typename T, int Step> +T *QRecyclePool<T, Step>::New() +{ + T *rv = d->allocate(); + new (rv) T; + return rv; +} + +template<typename T, int Step> +template<typename T1> +T *QRecyclePool<T, Step>::New(const T1 &a) +{ + T *rv = d->allocate(); + new (rv) T(a); + return rv; +} + +template<typename T, int Step> +template<typename T1> +T *QRecyclePool<T, Step>::New(T1 &a) +{ + T *rv = d->allocate(); + new (rv) T(a); + return rv; +} + +template<typename T, int Step> +void QRecyclePool<T, Step>::Delete(T *t) +{ + t->~T(); + QRecyclePoolPrivate<T, Step>::dispose(t); +} + +template<typename T, int Step> +void QRecyclePoolPrivate<T, Step>::releaseIfPossible() +{ + if (recyclePoolHold || outstandingItems) + return; + + Page *p = currentPage; + while (p) { + Page *n = p->nextPage; + qFree(p); + p = n; + } + + delete this; +} + +template<typename T, int Step> +T *QRecyclePoolPrivate<T, Step>::allocate() +{ + PoolType *rv = 0; + if (nextAllocated) { + rv = nextAllocated; + nextAllocated = rv->nextAllocated; + } else if (currentPage && currentPage->free) { + rv = (PoolType *)(currentPage->array + (Step - currentPage->free) * sizeof(PoolType)); + currentPage->free--; + } else { + Page *p = (Page *)qMalloc(sizeof(Page)); + p->nextPage = currentPage; + p->free = Step; + currentPage = p; + + rv = (PoolType *)currentPage->array; + currentPage->free--; + } + + rv->pool = this; + ++outstandingItems; + return rv; +} + +template<typename T, int Step> +void QRecyclePoolPrivate<T, Step>::dispose(T *t) +{ + PoolType *pt = static_cast<PoolType *>(t); + Q_ASSERT(pt->pool && pt->pool->cookie == QRECYCLEPOOLCOOKIE); + + QRecyclePoolPrivate<T, Step> *This = pt->pool; + pt->nextAllocated = This->nextAllocated; + This->nextAllocated = pt; + --This->outstandingItems; + This->releaseIfPossible(); +} + +QT_END_NAMESPACE + +#endif // QRECYCLEPOOL_P_H diff --git a/src/qml/qml/parser/parser.pri b/src/qml/qml/parser/parser.pri new file mode 100644 index 0000000000..6be85ba85a --- /dev/null +++ b/src/qml/qml/parser/parser.pri @@ -0,0 +1,19 @@ +HEADERS += \ + $$PWD/qqmljsast_p.h \ + $$PWD/qqmljsastfwd_p.h \ + $$PWD/qqmljsastvisitor_p.h \ + $$PWD/qqmljsengine_p.h \ + $$PWD/qqmljsgrammar_p.h \ + $$PWD/qqmljslexer_p.h \ + $$PWD/qqmljsmemorypool_p.h \ + $$PWD/qqmljsparser_p.h \ + $$PWD/qqmljsglobal_p.h \ + $$PWD/qqmljskeywords_p.h + +SOURCES += \ + $$PWD/qqmljsast.cpp \ + $$PWD/qqmljsastvisitor.cpp \ + $$PWD/qqmljsengine_p.cpp \ + $$PWD/qqmljsgrammar.cpp \ + $$PWD/qqmljslexer.cpp \ + $$PWD/qqmljsparser.cpp diff --git a/src/qml/qml/parser/qqmljs.g b/src/qml/qml/parser/qqmljs.g new file mode 100644 index 0000000000..746fcb24df --- /dev/null +++ b/src/qml/qml/parser/qqmljs.g @@ -0,0 +1,3016 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +-- Contact: http://www.qt-project.org/ +-- +-- This file is part of the QtQml module of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL-ONLY$ +-- GNU Lesser General Public License Usage +-- This file may be used under the terms of the GNU Lesser +-- General Public License version 2.1 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 2.1 requirements +-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +-- +-- If you have questions regarding the use of this file, please contact +-- us via http://www.qt-project.org/. +-- +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QQmlJSGrammar +%decl qqmljsparser_p.h +%impl qdeclarativejsparser.cpp +%expect 2 +%expect-rr 2 + +%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" +%token T_BREAK "break" T_CASE "case" T_CATCH "catch" +%token T_COLON ":" T_COMMA "," T_CONTINUE "continue" +%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/" +%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "." +%token T_ELSE "else" T_EQ "=" T_EQ_EQ "==" +%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for" +%token T_FUNCTION "function" T_GE ">=" T_GT ">" +%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>" +%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if" +%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{" +%token T_LBRACKET "[" T_LE "<=" T_LPAREN "(" +%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<=" +%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" +%token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" +%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" +%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" +%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" +%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" +%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" +%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly" +%token T_SWITCH "switch" T_THIS "this" T_THROW "throw" +%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" +%token T_VAR "var" T_VOID "void" T_WHILE "while" +%token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" +%token T_NULL "null" T_TRUE "true" T_FALSE "false" +%token T_CONST "const" +%token T_DEBUGGER "debugger" +%token T_RESERVED_WORD "reserved word" +%token T_MULTILINE_STRING_LITERAL "multiline string literal" +%token T_COMMENT "comment" + +--- context keywords. +%token T_PUBLIC "public" +%token T_IMPORT "import" +%token T_AS "as" +%token T_ON "on" + +%token T_ERROR + +--- feed tokens +%token T_FEED_UI_PROGRAM +%token T_FEED_UI_OBJECT_MEMBER +%token T_FEED_JS_STATEMENT +%token T_FEED_JS_EXPRESSION +%token T_FEED_JS_SOURCE_ELEMENT +%token T_FEED_JS_PROGRAM + +%nonassoc SHIFT_THERE +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc REDUCE_HERE + +%start TopLevel + +/./**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QtDebug> +#include <QtCore/QCoreApplication> + +#include <string.h> + +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsast_p.h" +#include "qqmljsmemorypool_p.h" + +./ + +/:/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// 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. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QDECLARATIVEJSPARSER_P_H +#define QDECLARATIVEJSPARSER_P_H + +#include "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include "qqmljsast_p.h" +#include "qqmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Parser: protected $table +{ +public: + union Value { + int ival; + double dval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline QStringRef &stringRef(int index) + { return string_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + MemoryPool *pool; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + QStringRef *string_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + QStringRef spell; + }; + + double yylval; + QStringRef yytokenspell; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QQmlJS + + +:/ + + +/. + +#include "qqmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QQmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef))); +} + +Parser::Parser(Engine *engine): + driver(engine), + pool(engine->pool()), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + string_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + free(sym_stack); + free(state_stack); + free(location_stack); + free(string_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->tokenStartLine(); + loc.startColumn = lexer->tokenStartColumn(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<QStringRef, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->tokenValue(); + yytokenspell = lexer->tokenSpell(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yytokenspell = first_token->spell; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + stringRef(1) = yytokenspell; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { +./ + +-------------------------------------------------------------------------------------------------------- +-- Declarative UI +-------------------------------------------------------------------------------------------------------- + +TopLevel: T_FEED_UI_PROGRAM UiProgram ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_STATEMENT Statement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_EXPRESSION Expression ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_PROGRAM Program ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +UiProgram: UiImportListOpt UiRootMember ; +/. +case $rule_number: { + sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; +./ + +UiImportListOpt: Empty ; +UiImportListOpt: UiImportList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; +./ + +UiImportList: UiImport ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport); +} break; +./ + +UiImportList: UiImportList UiImport ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport); +} break; +./ + +ImportId: MemberExpression ; + +UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->semicolonToken = loc(2); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = stringRef(4); + sym(1).UiImport->semicolonToken = loc(5); +} break; +./ + +UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = stringRef(3); + sym(1).UiImport->semicolonToken = loc(4); +} break; +./ + + +UiImportHead: T_IMPORT ImportId ; +/. +case $rule_number: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = new (pool) AST::UiImport(importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = new (pool) AST::UiImport(qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; +./ + +Empty: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiRootMember: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMemberList UiObjectMember ; +/. +case $rule_number: { + AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; +./ + +UiArrayMemberList: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); +} break; +./ + +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +/. +case $rule_number: { + AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectDefinition: UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiObjectDefinition ; + +UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; +./ + +UiScriptStatement: Block ; +UiScriptStatement: EmptyStatement ; +UiScriptStatement: ExpressionStatement ; +UiScriptStatement: IfStatement ; +UiScriptStatement: WithStatement ; +UiScriptStatement: SwitchStatement ; +UiScriptStatement: TryStatement ; + +UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ; +/. +case $rule_number: +{ + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiPropertyType: T_VAR ; +UiPropertyType: T_RESERVED_WORD ; +UiPropertyType: T_IDENTIFIER ; + +UiParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiParameterListOpt: UiParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; +./ + +UiParameterList: UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2)); + node->propertyTypeToken = loc(1); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4)); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3), + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; +./ + +UiObjectMember: VariableStatement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; +./ + +JsIdentifier: T_IDENTIFIER; + +JsIdentifier: T_PROPERTY ; +JsIdentifier: T_SIGNAL ; +JsIdentifier: T_READONLY ; +JsIdentifier: T_ON ; + +-------------------------------------------------------------------------------------------------------- +-- Expressions +-------------------------------------------------------------------------------------------------------- + +PrimaryExpression: T_THIS ; +/. +case $rule_number: { + AST::ThisExpression *node = new (pool) AST::ThisExpression(); + node->thisToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: JsIdentifier ; +/. +case $rule_number: { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NULL ; +/. +case $rule_number: { + AST::NullExpression *node = new (pool) AST::NullExpression(); + node->nullToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_TRUE ; +/. +case $rule_number: { + AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); + node->trueToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_FALSE ; +/. +case $rule_number: { + AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); + node->falseToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_MULTILINE_STRING_LITERAL ; +/.case $rule_number:./ + +PrimaryExpression: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_EQ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +-- PrimaryExpression: T_LBRACE T_RBRACE ; +-- /. +-- case $rule_number: { +-- sym(1).Node = new (pool) AST::ObjectLiteral(); +-- } break; +-- ./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + else + node = new (pool) AST::ObjectLiteral(); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +/. +case $rule_number: { + AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiQualifiedId: MemberExpression ; +/. +case $rule_number: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; +./ + +ElementList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); +} break; +./ + +ElementList: Elision AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); +} break; +./ + +ElementList: ElementList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ElementList: ElementList T_COMMA Elision AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +Elision: T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(); + node->commaToken = loc(1); + sym(1).Node = node; +} break; +./ + +Elision: Elision T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +PropertyName: T_IDENTIFIER %prec SHIFT_THERE ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_SIGNAL ; +/.case $rule_number:./ + +PropertyName: T_PROPERTY ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: ReservedIdentifier ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +ReservedIdentifier: T_BREAK ; +ReservedIdentifier: T_CASE ; +ReservedIdentifier: T_CATCH ; +ReservedIdentifier: T_CONTINUE ; +ReservedIdentifier: T_DEFAULT ; +ReservedIdentifier: T_DELETE ; +ReservedIdentifier: T_DO ; +ReservedIdentifier: T_ELSE ; +ReservedIdentifier: T_FALSE ; +ReservedIdentifier: T_FINALLY ; +ReservedIdentifier: T_FOR ; +ReservedIdentifier: T_FUNCTION ; +ReservedIdentifier: T_IF ; +ReservedIdentifier: T_IN ; +ReservedIdentifier: T_INSTANCEOF ; +ReservedIdentifier: T_NEW ; +ReservedIdentifier: T_NULL ; +ReservedIdentifier: T_RETURN ; +ReservedIdentifier: T_SWITCH ; +ReservedIdentifier: T_THIS ; +ReservedIdentifier: T_THROW ; +ReservedIdentifier: T_TRUE ; +ReservedIdentifier: T_TRY ; +ReservedIdentifier: T_TYPEOF ; +ReservedIdentifier: T_VAR ; +ReservedIdentifier: T_VOID ; +ReservedIdentifier: T_WHILE ; +ReservedIdentifier: T_CONST ; +ReservedIdentifier: T_DEBUGGER ; +ReservedIdentifier: T_RESERVED_WORD ; +ReservedIdentifier: T_WITH ; + +PropertyIdentifier: JsIdentifier ; +PropertyIdentifier: ReservedIdentifier ; + +MemberExpression: PrimaryExpression ; +MemberExpression: FunctionExpression ; + +MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; +./ + +NewExpression: MemberExpression ; + +NewExpression: T_NEW NewExpression ; +/. +case $rule_number: { + AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; +./ + +CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +ArgumentListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ArgumentListOpt: ArgumentList ; +/. +case $rule_number: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; +./ + +ArgumentList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +LeftHandSideExpression: NewExpression ; +LeftHandSideExpression: CallExpression ; +PostfixExpression: LeftHandSideExpression ; + +PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +/. +case $rule_number: { + AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +/. +case $rule_number: { + AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +UnaryExpression: PostfixExpression ; + +UnaryExpression: T_DELETE UnaryExpression ; +/. +case $rule_number: { + AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_VOID UnaryExpression ; +/. +case $rule_number: { + AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TYPEOF UnaryExpression ; +/. +case $rule_number: { + AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TILDE UnaryExpression ; +/. +case $rule_number: { + AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_NOT UnaryExpression ; +/. +case $rule_number: { + AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: UnaryExpression ; + +MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: MultiplicativeExpression ; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: AdditiveExpression ; + +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: ShiftExpression ; + +RelationalExpression: RelationalExpression T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_IN ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: ShiftExpression ; + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: RelationalExpression ; + +EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: RelationalExpressionNotIn ; + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpression: EqualityExpression ; + +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; + +BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpression: BitwiseANDExpression ; + +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; + +BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpression: BitwiseXORExpression ; + +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; + +BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpression: BitwiseORExpression ; + +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; + +LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpression: LogicalANDExpression ; + +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; + +LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ConditionalExpression: LogicalORExpression ; + +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +ConditionalExpressionNotIn: LogicalORExpressionNotIn ; + +ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +AssignmentExpression: ConditionalExpression ; + +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentExpressionNotIn: ConditionalExpressionNotIn ; + +AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentOperator: T_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::Assign; +} break; +./ + +AssignmentOperator: T_STAR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMul; +} break; +./ + +AssignmentOperator: T_DIVIDE_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceDiv; +} break; +./ + +AssignmentOperator: T_REMAINDER_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMod; +} break; +./ + +AssignmentOperator: T_PLUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAdd; +} break; +./ + +AssignmentOperator: T_MINUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceSub; +} break; +./ + +AssignmentOperator: T_LT_LT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; +./ + +AssignmentOperator: T_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; +./ + +AssignmentOperator: T_GT_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; +./ + +AssignmentOperator: T_AND_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAnd; +} break; +./ + +AssignmentOperator: T_XOR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceXor; +} break; +./ + +AssignmentOperator: T_OR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceOr; +} break; +./ + +Expression: AssignmentExpression ; + +Expression: Expression T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionOpt: Expression ; + +ExpressionNotIn: AssignmentExpressionNotIn ; + +ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionNotInOpt: ExpressionNotIn ; + +Statement: Block ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + + +Block: T_LBRACE StatementListOpt T_RBRACE ; +/. +case $rule_number: { + AST::Block *node = new (pool) AST::Block(sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +StatementList: Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); +} break; +./ + +StatementList: StatementList Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); +} break; +./ + +StatementListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +StatementListOpt: StatementList ; +/. +case $rule_number: { + sym(1).Node = sym(1).StatementList->finish (); +} break; +./ + +VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +/. +case $rule_number: { + AST::VariableStatement *node = new (pool) AST::VariableStatement( + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +VariableDeclarationKind: T_CONST ; +/. +case $rule_number: { + sym(1).ival = T_CONST; +} break; +./ + +VariableDeclarationKind: T_VAR ; +/. +case $rule_number: { + sym(1).ival = T_VAR; +} break; +./ + +VariableDeclarationList: VariableDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +/. +case $rule_number: { + AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; +./ + +VariableDeclaration: JsIdentifier InitialiserOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +Initialiser: T_EQ AssignmentExpression ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserOpt: Initialiser ; + +InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserNotInOpt: InitialiserNotIn ; + +EmptyStatement: T_SEMICOLON ; +/. +case $rule_number: { + AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; +./ + +ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ExpressionStatement: Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +/. +case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(6); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + + +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForStatement *node = new (pool) AST::LocalForStatement( + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +/. +case $rule_number: { + AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +/. +case $rule_number: { + AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; +./ + +CaseClauses: CaseClause ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); +} break; +./ + +CaseClauses: CaseClauses CaseClause ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); +} break; +./ + +CaseClausesOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +CaseClausesOpt: CaseClauses ; +/. +case $rule_number: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; +./ + +CaseClause: T_CASE Expression T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; +./ + +DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_SIGNAL T_COLON Statement ; +/.case $rule_number:./ + +LabelledStatement: T_PROPERTY T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_IDENTIFIER T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ThrowStatement: T_THROW Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Finally ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch Finally ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ; +/. +case $rule_number: { + AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +Finally: T_FINALLY Block ; +/. +case $rule_number: { + AST::Finally *node = new (pool) AST::Finally(sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; +./ + +DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +DebuggerStatement: T_DEBUGGER T_SEMICOLON ; +/. +case $rule_number: { + AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (! stringRef(2).isNull()) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FormalParameterList: JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +FormalParameterList: FormalParameterList T_COMMA JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +FormalParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FormalParameterListOpt: FormalParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; +./ + +FunctionBodyOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FunctionBodyOpt: FunctionBody ; + +FunctionBody: SourceElements ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); +} break; +./ + +Program: Empty ; + +Program: SourceElements ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); +} break; +./ + +SourceElements: SourceElement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); +} break; +./ + +SourceElements: SourceElements SourceElement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); +} break; +./ + +SourceElement: Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); +} break; +./ + +SourceElement: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); +} break; +./ + +IdentifierOpt: ; +/. +case $rule_number: { + stringRef(1) = QStringRef(); +} break; +./ + +IdentifierOpt: JsIdentifier ; + +PropertyNameAndValueListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +PropertyNameAndValueListOpt: PropertyNameAndValueList ; + +/. + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.spell = yytokenspell; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = qApp->translate("QQmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].spell = yytokenspell; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->tokenValue(); + token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QQmlParser", "Syntax error"); + else + msg = qApp->translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = qApp->translate("QQmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + +./ +/: +QT_QML_END_NAMESPACE + + + +#endif // QDECLARATIVEJSPARSER_P_H +:/ diff --git a/src/qml/qml/parser/qqmljsast.cpp b/src/qml/qml/parser/qqmljsast.cpp new file mode 100644 index 0000000000..d0b984fc9e --- /dev/null +++ b/src/qml/qml/parser/qqmljsast.cpp @@ -0,0 +1,931 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmljsast_p.h" + +#include "qqmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +void Node::accept(Visitor *visitor) +{ + if (visitor->preVisit(this)) { + accept0(visitor); + } + visitor->postVisit(this); +} + +void Node::accept(Node *node, Visitor *visitor) +{ + if (node) + node->accept(visitor); +} + +ExpressionNode *Node::expressionCast() +{ + return 0; +} + +BinaryExpression *Node::binaryExpressionCast() +{ + return 0; +} + +Statement *Node::statementCast() +{ + return 0; +} + +UiObjectMember *Node::uiObjectMemberCast() +{ + return 0; +} + +ExpressionNode *ExpressionNode::expressionCast() +{ + return this; +} + +BinaryExpression *BinaryExpression::binaryExpressionCast() +{ + return this; +} + +Statement *Statement::statementCast() +{ + return this; +} + +UiObjectMember *UiObjectMember::uiObjectMemberCast() +{ + return this; +} + +void NestedExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + visitor->endVisit(this); +} + +void ThisExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void IdentifierExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NullExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void TrueLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void FalseLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void RegExpLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + accept(elision, visitor); + } + + visitor->endVisit(this); +} + +void ObjectLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(properties, visitor); + } + + visitor->endVisit(this); +} + +void ElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ElementList *it = this; it; it = it->next) { + accept(it->elision, visitor); + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void Elision::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void PropertyNameAndValueList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (PropertyNameAndValueList *it = this; it; it = it->next) { + accept(it->name, visitor); + accept(it->value, visitor); + } + } + + visitor->endVisit(this); +} + +void IdentifierPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void FieldMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void NewMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void NewExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void CallExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void ArgumentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ArgumentList *it = this; it; it = it->next) { + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void PostIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void PostDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void DeleteExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void VoidExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TypeOfExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryPlusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryMinusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TildeExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void NotExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void BinaryExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void ConditionalExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void Expression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void Block::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void StatementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (StatementList *it = this; it; it = it->next) { + accept(it->statement, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + } + + visitor->endVisit(this); +} + +void VariableDeclarationList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (VariableDeclarationList *it = this; it; it = it->next) { + accept(it->declaration, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void EmptyStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ExpressionStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void IfStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void DoWhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ContinueStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void BreakStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ReturnStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WithStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void SwitchStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(block, visitor); + } + + visitor->endVisit(this); +} + +void CaseBlock::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(clauses, visitor); + accept(defaultClause, visitor); + accept(moreClauses, visitor); + } + + visitor->endVisit(this); +} + +void CaseClauses::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (CaseClauses *it = this; it; it = it->next) { + accept(it->clause, visitor); + } + } + + visitor->endVisit(this); +} + +void CaseClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void DefaultClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void LabelledStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ThrowStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TryStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(catchExpression, visitor); + accept(finallyExpression, visitor); + } + + visitor->endVisit(this); +} + +void Catch::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void Finally::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void FunctionDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FunctionExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FormalParameterList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void FunctionBody::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void Program::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void SourceElements::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (SourceElements *it = this; it; it = it->next) { + accept(it->element, visitor); + } + } + + visitor->endVisit(this); +} + +void FunctionSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + } + + visitor->endVisit(this); +} + +void StatementSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void DebuggerStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiProgram::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(imports, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiPublicMember::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(binding, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectDefinition::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectInitializer::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiScriptBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiObjectMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiArrayMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiQualifiedId::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiImport::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(importUri, visitor); + } + + visitor->endVisit(this); +} + +void UiImportList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(import, visitor); + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void UiSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(sourceElement, visitor); + } + + visitor->endVisit(this); +} + +} } // namespace QQmlJS::AST + +QT_QML_END_NAMESPACE + + diff --git a/src/qml/qml/parser/qqmljsast_p.h b/src/qml/qml/parser/qqmljsast_p.h new file mode 100644 index 0000000000..f85eb4ca5f --- /dev/null +++ b/src/qml/qml/parser/qqmljsast_p.h @@ -0,0 +1,2640 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSAST_P_H +#define QQMLJSAST_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 "qqmljsastvisitor_p.h" +#include "qqmljsglobal_p.h" +#include "qqmljsmemorypool_p.h" + +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +#define QQMLJS_DECLARE_AST_NODE(name) \ + enum { K = Kind_##name }; + +namespace QSOperator // ### rename +{ + +enum Op { + Add, + And, + InplaceAnd, + Assign, + BitAnd, + BitOr, + BitXor, + InplaceSub, + Div, + InplaceDiv, + Equal, + Ge, + Gt, + In, + InplaceAdd, + InstanceOf, + Le, + LShift, + InplaceLeftShift, + Lt, + Mod, + InplaceMod, + Mul, + InplaceMul, + NotEqual, + Or, + InplaceOr, + RShift, + InplaceRightShift, + StrictEqual, + StrictNotEqual, + Sub, + URShift, + InplaceURightShift, + InplaceXor +}; + +} // namespace QSOperator + +namespace QQmlJS { + +namespace AST { + +template <typename _T1, typename _T2> +_T1 cast(_T2 *ast) +{ + if (ast && ast->kind == static_cast<_T1>(0)->K) + return static_cast<_T1>(ast); + + return 0; +} + +class QML_PARSER_EXPORT Node: public Managed +{ +public: + enum Kind { + Kind_Undefined, + + Kind_ArgumentList, + Kind_ArrayLiteral, + Kind_ArrayMemberExpression, + Kind_BinaryExpression, + Kind_Block, + Kind_BreakStatement, + Kind_CallExpression, + Kind_CaseBlock, + Kind_CaseClause, + Kind_CaseClauses, + Kind_Catch, + Kind_ConditionalExpression, + Kind_ContinueStatement, + Kind_DebuggerStatement, + Kind_DefaultClause, + Kind_DeleteExpression, + Kind_DoWhileStatement, + Kind_ElementList, + Kind_Elision, + Kind_EmptyStatement, + Kind_Expression, + Kind_ExpressionStatement, + Kind_FalseLiteral, + Kind_FieldMemberExpression, + Kind_Finally, + Kind_ForEachStatement, + Kind_ForStatement, + Kind_FormalParameterList, + Kind_FunctionBody, + Kind_FunctionDeclaration, + Kind_FunctionExpression, + Kind_FunctionSourceElement, + Kind_IdentifierExpression, + Kind_IdentifierPropertyName, + Kind_IfStatement, + Kind_LabelledStatement, + Kind_LocalForEachStatement, + Kind_LocalForStatement, + Kind_NewExpression, + Kind_NewMemberExpression, + Kind_NotExpression, + Kind_NullExpression, + Kind_NumericLiteral, + Kind_NumericLiteralPropertyName, + Kind_ObjectLiteral, + Kind_PostDecrementExpression, + Kind_PostIncrementExpression, + Kind_PreDecrementExpression, + Kind_PreIncrementExpression, + Kind_Program, + Kind_PropertyName, + Kind_PropertyNameAndValueList, + Kind_RegExpLiteral, + Kind_ReturnStatement, + Kind_SourceElement, + Kind_SourceElements, + Kind_StatementList, + Kind_StatementSourceElement, + Kind_StringLiteral, + Kind_StringLiteralPropertyName, + Kind_SwitchStatement, + Kind_ThisExpression, + Kind_ThrowStatement, + Kind_TildeExpression, + Kind_TrueLiteral, + Kind_TryStatement, + Kind_TypeOfExpression, + Kind_UnaryMinusExpression, + Kind_UnaryPlusExpression, + Kind_VariableDeclaration, + Kind_VariableDeclarationList, + Kind_VariableStatement, + Kind_VoidExpression, + Kind_WhileStatement, + Kind_WithStatement, + Kind_NestedExpression, + + Kind_UiArrayBinding, + Kind_UiImport, + Kind_UiImportList, + Kind_UiObjectBinding, + Kind_UiObjectDefinition, + Kind_UiObjectInitializer, + Kind_UiObjectMemberList, + Kind_UiArrayMemberList, + Kind_UiProgram, + Kind_UiParameterList, + Kind_UiPublicMember, + Kind_UiQualifiedId, + Kind_UiScriptBinding, + Kind_UiSourceElement + }; + + inline Node() + : kind(Kind_Undefined) {} + + // NOTE: node destructors are never called, + // instead we block free the memory + // (see the NodePool class) + virtual ~Node() {} + + virtual ExpressionNode *expressionCast(); + virtual BinaryExpression *binaryExpressionCast(); + virtual Statement *statementCast(); + virtual UiObjectMember *uiObjectMemberCast(); + + void accept(Visitor *visitor); + static void accept(Node *node, Visitor *visitor); + + inline static void acceptChild(Node *node, Visitor *visitor) + { return accept(node, visitor); } // ### remove + + virtual void accept0(Visitor *visitor) = 0; + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + +// attributes + int kind; +}; + +class QML_PARSER_EXPORT ExpressionNode: public Node +{ +public: + ExpressionNode() {} + + virtual ExpressionNode *expressionCast(); +}; + +class QML_PARSER_EXPORT Statement: public Node +{ +public: + Statement() {} + + virtual Statement *statementCast(); +}; + +class QML_PARSER_EXPORT NestedExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NestedExpression) + + NestedExpression(ExpressionNode *expression) + : expression(expression) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lparenToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *expression; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ThisExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ThisExpression) + + ThisExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return thisToken; } + + virtual SourceLocation lastSourceLocation() const + { return thisToken; } + +// attributes + SourceLocation thisToken; +}; + +class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(IdentifierExpression) + + IdentifierExpression(const QStringRef &n): + name (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + +// attributes + QStringRef name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NullExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NullExpression) + + NullExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return nullToken; } + + virtual SourceLocation lastSourceLocation() const + { return nullToken; } + +// attributes + SourceLocation nullToken; +}; + +class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TrueLiteral) + + TrueLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return trueToken; } + + virtual SourceLocation lastSourceLocation() const + { return trueToken; } + +// attributes + SourceLocation trueToken; +}; + +class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FalseLiteral) + + FalseLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return falseToken; } + + virtual SourceLocation lastSourceLocation() const + { return falseToken; } + +// attributes + SourceLocation falseToken; +}; + +class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NumericLiteral) + + NumericLiteral(double v): + value(v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + double value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(StringLiteral) + + StringLiteral(const QStringRef &v): + value (v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + QStringRef value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(RegExpLiteral) + + RegExpLiteral(const QStringRef &p, int f): + pattern (p), flags (f) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + QStringRef pattern; + int flags; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ArrayLiteral) + + ArrayLiteral(Elision *e): + elements (0), elision (e) + { kind = K; } + + ArrayLiteral(ElementList *elts): + elements (elts), elision (0) + { kind = K; } + + ArrayLiteral(ElementList *elts, Elision *e): + elements (elts), elision (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbracketToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ElementList *elements; + Elision *elision; + SourceLocation lbracketToken; + SourceLocation commaToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ObjectLiteral) + + ObjectLiteral(): + properties (0) { kind = K; } + + ObjectLiteral(PropertyNameAndValueList *plist): + properties (plist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + PropertyNameAndValueList *properties; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT Elision: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Elision) + + Elision(): + next (this) { kind = K; } + + Elision(Elision *previous) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return commaToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : commaToken; } + + inline Elision *finish () + { + Elision *front = next; + next = 0; + return front; + } + +// attributes + Elision *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT ElementList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(ElementList) + + ElementList(Elision *e, ExpressionNode *expr): + elision (e), expression (expr), next (this) + { kind = K; } + + ElementList(ElementList *previous, Elision *e, ExpressionNode *expr): + elision (e), expression (expr) + { + kind = K; + next = previous->next; + previous->next = this; + } + + inline ElementList *finish () + { + ElementList *front = next; + next = 0; + return front; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (elision) + return elision->firstSourceLocation(); + return expression->firstSourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return expression->lastSourceLocation(); + } + +// attributes + Elision *elision; + ExpressionNode *expression; + ElementList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyName: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyName) + + PropertyName() { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return propertyNameToken; } + + virtual SourceLocation lastSourceLocation() const + { return propertyNameToken; } + +// attributes + SourceLocation propertyNameToken; +}; + +class QML_PARSER_EXPORT PropertyNameAndValueList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyNameAndValueList) + + PropertyNameAndValueList(PropertyName *n, ExpressionNode *v): + name (n), value (v), next (this) + { kind = K; } + + PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v): + name (n), value (v) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return name->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return value->lastSourceLocation(); + } + + inline PropertyNameAndValueList *finish () + { + PropertyNameAndValueList *front = next; + next = 0; + return front; + } + +// attributes + PropertyName *name; + ExpressionNode *value; + PropertyNameAndValueList *next; + SourceLocation colonToken; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(IdentifierPropertyName) + + IdentifierPropertyName(const QStringRef &n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + QStringRef id; +}; + +class QML_PARSER_EXPORT StringLiteralPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(StringLiteralPropertyName) + + StringLiteralPropertyName(const QStringRef &n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + QStringRef id; +}; + +class QML_PARSER_EXPORT NumericLiteralPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(NumericLiteralPropertyName) + + NumericLiteralPropertyName(double n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + double id; +}; + +class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression) + + ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e): + base (b), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ExpressionNode *base; + ExpressionNode *expression; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FieldMemberExpression) + + FieldMemberExpression(ExpressionNode *b, const QStringRef &n): + base (b), name (n) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + + // attributes + ExpressionNode *base; + QStringRef name; + SourceLocation dotToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NewMemberExpression) + + NewMemberExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + + // attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation newToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT NewExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NewExpression) + + NewExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation newToken; +}; + +class QML_PARSER_EXPORT CallExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(CallExpression) + + CallExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ArgumentList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(ArgumentList) + + ArgumentList(ExpressionNode *e): + expression (e), next (this) + { kind = K; } + + ArgumentList(ArgumentList *previous, ExpressionNode *e): + expression (e) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return expression->lastSourceLocation(); + } + + inline ArgumentList *finish () + { + ArgumentList *front = next; + next = 0; + return front; + } + +// attributes + ExpressionNode *expression; + ArgumentList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PostIncrementExpression) + + PostIncrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return incrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PostDecrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PostDecrementExpression) + + PostDecrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return decrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT DeleteExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(DeleteExpression) + + DeleteExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return deleteToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation deleteToken; +}; + +class QML_PARSER_EXPORT VoidExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(VoidExpression) + + VoidExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return voidToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation voidToken; +}; + +class QML_PARSER_EXPORT TypeOfExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TypeOfExpression) + + TypeOfExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return typeofToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation typeofToken; +}; + +class QML_PARSER_EXPORT PreIncrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PreIncrementExpression) + + PreIncrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return incrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PreDecrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PreDecrementExpression) + + PreDecrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return decrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT UnaryPlusExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(UnaryPlusExpression) + + UnaryPlusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return plusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation plusToken; +}; + +class QML_PARSER_EXPORT UnaryMinusExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(UnaryMinusExpression) + + UnaryMinusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return minusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation minusToken; +}; + +class QML_PARSER_EXPORT TildeExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TildeExpression) + + TildeExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tildeToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation tildeToken; +}; + +class QML_PARSER_EXPORT NotExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NotExpression) + + NotExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return notToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation notToken; +}; + +class QML_PARSER_EXPORT BinaryExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(BinaryExpression) + + BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r): + left (l), op (o), right (r) + { kind = K; } + + virtual BinaryExpression *binaryExpressionCast(); + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + int op; + ExpressionNode *right; + SourceLocation operatorToken; +}; + +class QML_PARSER_EXPORT ConditionalExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ConditionalExpression) + + ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return ko->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + ExpressionNode *ok; + ExpressionNode *ko; + SourceLocation questionToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT Expression: public ExpressionNode // ### rename +{ +public: + QQMLJS_DECLARE_AST_NODE(Expression) + + Expression(ExpressionNode *l, ExpressionNode *r): + left (l), right (r) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + ExpressionNode *right; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT Block: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(Block) + + Block(StatementList *slist): + statements (slist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + + // attributes + StatementList *statements; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT StatementList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(StatementList) + + StatementList(Statement *stmt): + statement (stmt), next (this) + { kind = K; } + + StatementList(StatementList *previous, Statement *stmt): + statement (stmt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return statement->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : statement->lastSourceLocation(); } + + inline StatementList *finish () + { + StatementList *front = next; + next = 0; + return front; + } + +// attributes + Statement *statement; + StatementList *next; +}; + +class QML_PARSER_EXPORT VariableStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declarationKindToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + VariableDeclarationList *declarations; + SourceLocation declarationKindToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT VariableDeclaration: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableDeclaration) + + VariableDeclaration(const QStringRef &n, ExpressionNode *e): + name (n), expression (e), readOnly(false) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression ? expression->lastSourceLocation() : identifierToken; } + +// attributes + QStringRef name; + ExpressionNode *expression; + bool readOnly; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT VariableDeclarationList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableDeclarationList) + + VariableDeclarationList(VariableDeclaration *decl): + declaration (decl), next (this) + { kind = K; } + + VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl): + declaration (decl) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declaration->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return declaration->lastSourceLocation(); + } + + inline VariableDeclarationList *finish (bool readOnly) + { + VariableDeclarationList *front = next; + next = 0; + if (readOnly) { + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) + vdl->declaration->readOnly = true; + } + return front; + } + +// attributes + VariableDeclaration *declaration; + VariableDeclarationList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT EmptyStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(EmptyStatement) + + EmptyStatement() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return semicolonToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ExpressionStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ExpressionStatement) + + ExpressionStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT IfStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(IfStatement) + + IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return ifToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (ko) + return ko->lastSourceLocation(); + + return ok->lastSourceLocation(); + } + +// attributes + ExpressionNode *expression; + Statement *ok; + Statement *ko; + SourceLocation ifToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation elseToken; +}; + +class QML_PARSER_EXPORT DoWhileStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(DoWhileStatement) + + DoWhileStatement(Statement *stmt, ExpressionNode *e): + statement (stmt), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return doToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + Statement *statement; + ExpressionNode *expression; + SourceLocation doToken; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WhileStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(WhileStatement) + + WhileStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return whileToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ForStatement) + + ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + initialiser (i), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LocalForStatement) + + LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + declarations (vlist), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclarationList *declarations; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForEachStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ForEachStatement) + + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): + initialiser (i), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForEachStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LocalForEachStatement) + + LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): + declaration (v), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclaration *declaration; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ContinueStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ContinueStatement) + + ContinueStatement(const QStringRef &l = QStringRef()): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return continueToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + QStringRef label; + SourceLocation continueToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT BreakStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(BreakStatement) + + BreakStatement(const QStringRef &l): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return breakToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + QStringRef label; + SourceLocation breakToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ReturnStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ReturnStatement) + + ReturnStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return returnToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation returnToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WithStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(WithStatement) + + WithStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return withToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation withToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseBlock: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseBlock) + + CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0): + clauses (c), defaultClause (d), moreClauses (r) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + CaseClauses *clauses; + DefaultClause *defaultClause; + CaseClauses *moreClauses; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT SwitchStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(SwitchStatement) + + SwitchStatement(ExpressionNode *e, CaseBlock *b): + expression (e), block (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return switchToken; } + + virtual SourceLocation lastSourceLocation() const + { return block->rbraceToken; } + +// attributes + ExpressionNode *expression; + CaseBlock *block; + SourceLocation switchToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseClause: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseClause) + + CaseClause(ExpressionNode *e, StatementList *slist): + expression (e), statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return caseToken; } + + virtual SourceLocation lastSourceLocation() const + { return statements ? statements->lastSourceLocation() : colonToken; } + +// attributes + ExpressionNode *expression; + StatementList *statements; + SourceLocation caseToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT CaseClauses: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseClauses) + + CaseClauses(CaseClause *c): + clause (c), next (this) + { kind = K; } + + CaseClauses(CaseClauses *previous, CaseClause *c): + clause (c) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return clause->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : clause->lastSourceLocation(); } + + inline CaseClauses *finish () + { + CaseClauses *front = next; + next = 0; + return front; + } + +//attributes + CaseClause *clause; + CaseClauses *next; +}; + +class QML_PARSER_EXPORT DefaultClause: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(DefaultClause) + + DefaultClause(StatementList *slist): + statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return defaultToken; } + + virtual SourceLocation lastSourceLocation() const + { return statements ? statements->lastSourceLocation() : colonToken; } + +// attributes + StatementList *statements; + SourceLocation defaultToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT LabelledStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LabelledStatement) + + LabelledStatement(const QStringRef &l, Statement *stmt): + label (l), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + QStringRef label; + Statement *statement; + SourceLocation identifierToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT ThrowStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ThrowStatement) + + ThrowStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return throwToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + ExpressionNode *expression; + SourceLocation throwToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT Catch: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Catch) + + Catch(const QStringRef &n, Block *stmt): + name (n), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return catchToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + QStringRef name; + Block *statement; + SourceLocation catchToken; + SourceLocation lparenToken; + SourceLocation identifierToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT Finally: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Finally) + + Finally(Block *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return finallyToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement ? statement->lastSourceLocation() : finallyToken; } + +// attributes + Block *statement; + SourceLocation finallyToken; +}; + +class QML_PARSER_EXPORT TryStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(TryStatement) + + TryStatement(Statement *stmt, Catch *c, Finally *f): + statement (stmt), catchExpression (c), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Finally *f): + statement (stmt), catchExpression (0), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Catch *c): + statement (stmt), catchExpression (c), finallyExpression (0) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tryToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (finallyExpression) + return finallyExpression->statement->rbraceToken; + else if (catchExpression) + return catchExpression->statement->rbraceToken; + + return statement->lastSourceLocation(); + } + +// attributes + Statement *statement; + Catch *catchExpression; + Finally *finallyExpression; + SourceLocation tryToken; +}; + +class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionExpression) + + FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + name (n), formals (f), body (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return functionToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + QStringRef name; + FormalParameterList *formals; + FunctionBody *body; + SourceLocation functionToken; + SourceLocation identifierToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionDeclaration) + + FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(n, f, b) + { kind = K; } + + virtual void accept0(Visitor *visitor); +}; + +class QML_PARSER_EXPORT FormalParameterList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(FormalParameterList) + + FormalParameterList(const QStringRef &n): + name (n), next (this) + { kind = K; } + + FormalParameterList(FormalParameterList *previous, const QStringRef &n): + name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + + inline FormalParameterList *finish () + { + FormalParameterList *front = next; + next = 0; + return front; + } + +// attributes + QStringRef name; + FormalParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT SourceElement: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(SourceElement) + + inline SourceElement() + { kind = K; } +}; + +class QML_PARSER_EXPORT SourceElements: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(SourceElements) + + SourceElements(SourceElement *elt): + element (elt), next (this) + { kind = K; } + + SourceElements(SourceElements *previous, SourceElement *elt): + element (elt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return element->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : element->lastSourceLocation(); } + + inline SourceElements *finish () + { + SourceElements *front = next; + next = 0; + return front; + } + +// attributes + SourceElement *element; + SourceElements *next; +}; + +class QML_PARSER_EXPORT FunctionBody: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionBody) + + FunctionBody(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return elements ? elements->firstSourceLocation() : SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return elements ? elements->lastSourceLocation() : SourceLocation(); } + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT Program: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Program) + + Program(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return elements ? elements->firstSourceLocation() : SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return elements ? elements->lastSourceLocation() : SourceLocation(); } + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionSourceElement) + + FunctionSourceElement(FunctionDeclaration *f): + declaration (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declaration->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return declaration->lastSourceLocation(); } + +// attributes + FunctionDeclaration *declaration; +}; + +class QML_PARSER_EXPORT StatementSourceElement: public SourceElement +{ +public: + QQMLJS_DECLARE_AST_NODE(StatementSourceElement) + + StatementSourceElement(Statement *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return statement->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + Statement *statement; +}; + +class QML_PARSER_EXPORT DebuggerStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(DebuggerStatement) + + DebuggerStatement() + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return debuggerToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation debuggerToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiQualifiedId: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiQualifiedId) + + UiQualifiedId(const QStringRef &name) + : next(this), name(name) + { kind = K; } + + UiQualifiedId(UiQualifiedId *previous, const QStringRef &name) + : name(name) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + +// attributes + UiQualifiedId *next; + QStringRef name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiImport: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiImport) + + UiImport(const QStringRef &fileName) + : fileName(fileName), importUri(0) + { kind = K; } + + UiImport(UiQualifiedId *uri) + : importUri(uri) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return importToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + QStringRef fileName; + UiQualifiedId *importUri; + QStringRef importId; + SourceLocation importToken; + SourceLocation fileNameToken; + SourceLocation versionToken; + SourceLocation asToken; + SourceLocation importIdToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiImportList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiImportList) + + UiImportList(UiImport *import) + : import(import), + next(this) + { kind = K; } + + UiImportList(UiImportList *previous, UiImport *import) + : import(import) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiImportList *finish() + { + UiImportList *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return import->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : import->lastSourceLocation(); } + +// attributes + UiImport *import; + UiImportList *next; +}; + +class QML_PARSER_EXPORT UiObjectMember: public Node +{ +public: + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + + virtual UiObjectMember *uiObjectMemberCast(); +}; + +class QML_PARSER_EXPORT UiObjectMemberList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectMemberList) + + UiObjectMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiObjectMemberList(UiObjectMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return member->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : member->lastSourceLocation(); } + + UiObjectMemberList *finish() + { + UiObjectMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiObjectMemberList *next; + UiObjectMember *member; +}; + +class QML_PARSER_EXPORT UiProgram: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiProgram) + + UiProgram(UiImportList *imports, UiObjectMemberList *members) + : imports(imports), members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (imports) + return imports->firstSourceLocation(); + else if (members) + return members->firstSourceLocation(); + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (members) + return members->lastSourceLocation(); + else if (imports) + return imports->lastSourceLocation(); + return SourceLocation(); + } + +// attributes + UiImportList *imports; + UiObjectMemberList *members; +}; + +class QML_PARSER_EXPORT UiArrayMemberList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiArrayMemberList) + + UiArrayMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiArrayMemberList(UiArrayMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return member->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : member->lastSourceLocation(); } + + UiArrayMemberList *finish() + { + UiArrayMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiArrayMemberList *next; + UiObjectMember *member; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT UiObjectInitializer: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectInitializer) + + UiObjectInitializer(UiObjectMemberList *members) + : members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + SourceLocation lbraceToken; + UiObjectMemberList *members; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT UiParameterList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiParameterList) + + UiParameterList(const QStringRef &t, const QStringRef &n): + type (t), name (n), next (this) + { kind = K; } + + UiParameterList(UiParameterList *previous, const QStringRef &t, const QStringRef &n): + type (t), name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *) {} + + virtual SourceLocation firstSourceLocation() const + { return propertyTypeToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + + inline UiParameterList *finish () + { + UiParameterList *front = next; + next = 0; + return front; + } + +// attributes + QStringRef type; + QStringRef name; + UiParameterList *next; + SourceLocation commaToken; + SourceLocation propertyTypeToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiPublicMember) + + UiPublicMember(const QStringRef &memberType, + const QStringRef &name) + : type(Property), memberType(memberType), name(name), statement(0), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + UiPublicMember(const QStringRef &memberType, + const QStringRef &name, + Statement *statement) + : type(Property), memberType(memberType), name(name), statement(statement), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (defaultToken.isValid()) + return defaultToken; + else if (readonlyToken.isValid()) + return readonlyToken; + + return propertyToken; + } + + virtual SourceLocation lastSourceLocation() const + { + if (binding) + return binding->lastSourceLocation(); + if (statement) + return statement->lastSourceLocation(); + + return semicolonToken; + } + +// attributes + enum { Signal, Property } type; + QStringRef typeModifier; + QStringRef memberType; + QStringRef name; + Statement *statement; // initialized with a JS expression + UiObjectMember *binding; // initialized with a QML object or array. + bool isDefaultMember; + bool isReadonlyMember; + UiParameterList *parameters; + SourceLocation defaultToken; + SourceLocation readonlyToken; + SourceLocation propertyToken; + SourceLocation typeModifierToken; + SourceLocation typeToken; + SourceLocation identifierToken; + SourceLocation colonToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectDefinition) + + UiObjectDefinition(UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedTypeNameId(qualifiedTypeNameId), initializer(initializer) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return qualifiedTypeNameId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + +// attributes + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; +}; + +class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiSourceElement) + + UiSourceElement(Node *sourceElement) + : sourceElement(sourceElement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->lastSourceLocation(); + + return SourceLocation(); + } + + virtual void accept0(Visitor *visitor); + + +// attributes + Node *sourceElement; +}; + +class QML_PARSER_EXPORT UiObjectBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectBinding) + + UiObjectBinding(UiQualifiedId *qualifiedId, + UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedId(qualifiedId), + qualifiedTypeNameId(qualifiedTypeNameId), + initializer(initializer), + hasOnToken(false) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (hasOnToken && qualifiedTypeNameId) + return qualifiedTypeNameId->identifierToken; + + return qualifiedId->identifierToken; + } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + + +// attributes + UiQualifiedId *qualifiedId; + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; + SourceLocation colonToken; + bool hasOnToken; +}; + +class QML_PARSER_EXPORT UiScriptBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiScriptBinding) + + UiScriptBinding(UiQualifiedId *qualifiedId, + Statement *statement) + : qualifiedId(qualifiedId), + statement(statement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + Statement *statement; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT UiArrayBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiArrayBinding) + + UiArrayBinding(UiQualifiedId *qualifiedId, + UiArrayMemberList *members) + : qualifiedId(qualifiedId), + members(members) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiArrayMemberList *members; + SourceLocation colonToken; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +} } // namespace AST + + + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/qml/qml/parser/qqmljsastfwd_p.h b/src/qml/qml/parser/qqmljsastfwd_p.h new file mode 100644 index 0000000000..dec1cbc599 --- /dev/null +++ b/src/qml/qml/parser/qqmljsastfwd_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSAST_FWD_P_H +#define QQMLJSAST_FWD_P_H + +#include "qqmljsglobal_p.h" + +#include <QtCore/qglobal.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. +// + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +class SourceLocation +{ +public: + SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) + : offset(offset), length(length), + startLine(line), startColumn(column) + { } + + bool isValid() const { return length != 0; } + + quint32 begin() const { return offset; } + quint32 end() const { return offset + length; } + +// attributes + // ### encode + quint32 offset; + quint32 length; + quint32 startLine; + quint32 startColumn; +}; + +class Visitor; +class Node; +class ExpressionNode; +class Statement; +class ThisExpression; +class IdentifierExpression; +class NullExpression; +class TrueLiteral; +class FalseLiteral; +class NumericLiteral; +class StringLiteral; +class RegExpLiteral; +class ArrayLiteral; +class ObjectLiteral; +class ElementList; +class Elision; +class PropertyNameAndValueList; +class PropertyName; +class IdentifierPropertyName; +class StringLiteralPropertyName; +class NumericLiteralPropertyName; +class ArrayMemberExpression; +class FieldMemberExpression; +class NewMemberExpression; +class NewExpression; +class CallExpression; +class ArgumentList; +class PostIncrementExpression; +class PostDecrementExpression; +class DeleteExpression; +class VoidExpression; +class TypeOfExpression; +class PreIncrementExpression; +class PreDecrementExpression; +class UnaryPlusExpression; +class UnaryMinusExpression; +class TildeExpression; +class NotExpression; +class BinaryExpression; +class ConditionalExpression; +class Expression; // ### rename +class Block; +class StatementList; +class VariableStatement; +class VariableDeclarationList; +class VariableDeclaration; +class EmptyStatement; +class ExpressionStatement; +class IfStatement; +class DoWhileStatement; +class WhileStatement; +class ForStatement; +class LocalForStatement; +class ForEachStatement; +class LocalForEachStatement; +class ContinueStatement; +class BreakStatement; +class ReturnStatement; +class WithStatement; +class SwitchStatement; +class CaseBlock; +class CaseClauses; +class CaseClause; +class DefaultClause; +class LabelledStatement; +class ThrowStatement; +class TryStatement; +class Catch; +class Finally; +class FunctionDeclaration; +class FunctionExpression; +class FormalParameterList; +class FunctionBody; +class Program; +class SourceElements; +class SourceElement; +class FunctionSourceElement; +class StatementSourceElement; +class DebuggerStatement; +class NestedExpression; + +// ui elements +class UiProgram; +class UiImportList; +class UiImport; +class UiPublicMember; +class UiObjectDefinition; +class UiObjectInitializer; +class UiObjectBinding; +class UiScriptBinding; +class UiSourceElement; +class UiArrayBinding; +class UiObjectMember; +class UiObjectMemberList; +class UiArrayMemberList; +class UiQualifiedId; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/qml/qml/parser/qqmljsastvisitor.cpp b/src/qml/qml/parser/qqmljsastvisitor.cpp new file mode 100644 index 0000000000..2d854dc735 --- /dev/null +++ b/src/qml/qml/parser/qqmljsastvisitor.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +} } // namespace QQmlJS::AST + +QT_QML_END_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsastvisitor_p.h b/src/qml/qml/parser/qqmljsastvisitor_p.h new file mode 100644 index 0000000000..991580309d --- /dev/null +++ b/src/qml/qml/parser/qqmljsastvisitor_p.h @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSASTVISITOR_P_H +#define QQMLJSASTVISITOR_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 "qqmljsastfwd_p.h" +#include "qqmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +class QML_PARSER_EXPORT Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual bool preVisit(Node *) { return true; } + virtual void postVisit(Node *) {} + + // Ui + virtual bool visit(UiProgram *) { return true; } + virtual bool visit(UiImportList *) { return true; } + virtual bool visit(UiImport *) { return true; } + virtual bool visit(UiPublicMember *) { return true; } + virtual bool visit(UiSourceElement *) { return true; } + virtual bool visit(UiObjectDefinition *) { return true; } + virtual bool visit(UiObjectInitializer *) { return true; } + virtual bool visit(UiObjectBinding *) { return true; } + virtual bool visit(UiScriptBinding *) { return true; } + virtual bool visit(UiArrayBinding *) { return true; } + virtual bool visit(UiObjectMemberList *) { return true; } + virtual bool visit(UiArrayMemberList *) { return true; } + virtual bool visit(UiQualifiedId *) { return true; } + + virtual void endVisit(UiProgram *) {} + virtual void endVisit(UiImportList *) {} + virtual void endVisit(UiImport *) {} + virtual void endVisit(UiPublicMember *) {} + virtual void endVisit(UiSourceElement *) {} + virtual void endVisit(UiObjectDefinition *) {} + virtual void endVisit(UiObjectInitializer *) {} + virtual void endVisit(UiObjectBinding *) {} + virtual void endVisit(UiScriptBinding *) {} + virtual void endVisit(UiArrayBinding *) {} + virtual void endVisit(UiObjectMemberList *) {} + virtual void endVisit(UiArrayMemberList *) {} + virtual void endVisit(UiQualifiedId *) {} + + // QQmlJS + virtual bool visit(ThisExpression *) { return true; } + virtual void endVisit(ThisExpression *) {} + + virtual bool visit(IdentifierExpression *) { return true; } + virtual void endVisit(IdentifierExpression *) {} + + virtual bool visit(NullExpression *) { return true; } + virtual void endVisit(NullExpression *) {} + + virtual bool visit(TrueLiteral *) { return true; } + virtual void endVisit(TrueLiteral *) {} + + virtual bool visit(FalseLiteral *) { return true; } + virtual void endVisit(FalseLiteral *) {} + + virtual bool visit(StringLiteral *) { return true; } + virtual void endVisit(StringLiteral *) {} + + virtual bool visit(NumericLiteral *) { return true; } + virtual void endVisit(NumericLiteral *) {} + + virtual bool visit(RegExpLiteral *) { return true; } + virtual void endVisit(RegExpLiteral *) {} + + virtual bool visit(ArrayLiteral *) { return true; } + virtual void endVisit(ArrayLiteral *) {} + + virtual bool visit(ObjectLiteral *) { return true; } + virtual void endVisit(ObjectLiteral *) {} + + virtual bool visit(ElementList *) { return true; } + virtual void endVisit(ElementList *) {} + + virtual bool visit(Elision *) { return true; } + virtual void endVisit(Elision *) {} + + virtual bool visit(PropertyNameAndValueList *) { return true; } + virtual void endVisit(PropertyNameAndValueList *) {} + + virtual bool visit(NestedExpression *) { return true; } + virtual void endVisit(NestedExpression *) {} + + virtual bool visit(IdentifierPropertyName *) { return true; } + virtual void endVisit(IdentifierPropertyName *) {} + + virtual bool visit(StringLiteralPropertyName *) { return true; } + virtual void endVisit(StringLiteralPropertyName *) {} + + virtual bool visit(NumericLiteralPropertyName *) { return true; } + virtual void endVisit(NumericLiteralPropertyName *) {} + + virtual bool visit(ArrayMemberExpression *) { return true; } + virtual void endVisit(ArrayMemberExpression *) {} + + virtual bool visit(FieldMemberExpression *) { return true; } + virtual void endVisit(FieldMemberExpression *) {} + + virtual bool visit(NewMemberExpression *) { return true; } + virtual void endVisit(NewMemberExpression *) {} + + virtual bool visit(NewExpression *) { return true; } + virtual void endVisit(NewExpression *) {} + + virtual bool visit(CallExpression *) { return true; } + virtual void endVisit(CallExpression *) {} + + virtual bool visit(ArgumentList *) { return true; } + virtual void endVisit(ArgumentList *) {} + + virtual bool visit(PostIncrementExpression *) { return true; } + virtual void endVisit(PostIncrementExpression *) {} + + virtual bool visit(PostDecrementExpression *) { return true; } + virtual void endVisit(PostDecrementExpression *) {} + + virtual bool visit(DeleteExpression *) { return true; } + virtual void endVisit(DeleteExpression *) {} + + virtual bool visit(VoidExpression *) { return true; } + virtual void endVisit(VoidExpression *) {} + + virtual bool visit(TypeOfExpression *) { return true; } + virtual void endVisit(TypeOfExpression *) {} + + virtual bool visit(PreIncrementExpression *) { return true; } + virtual void endVisit(PreIncrementExpression *) {} + + virtual bool visit(PreDecrementExpression *) { return true; } + virtual void endVisit(PreDecrementExpression *) {} + + virtual bool visit(UnaryPlusExpression *) { return true; } + virtual void endVisit(UnaryPlusExpression *) {} + + virtual bool visit(UnaryMinusExpression *) { return true; } + virtual void endVisit(UnaryMinusExpression *) {} + + virtual bool visit(TildeExpression *) { return true; } + virtual void endVisit(TildeExpression *) {} + + virtual bool visit(NotExpression *) { return true; } + virtual void endVisit(NotExpression *) {} + + virtual bool visit(BinaryExpression *) { return true; } + virtual void endVisit(BinaryExpression *) {} + + virtual bool visit(ConditionalExpression *) { return true; } + virtual void endVisit(ConditionalExpression *) {} + + virtual bool visit(Expression *) { return true; } + virtual void endVisit(Expression *) {} + + virtual bool visit(Block *) { return true; } + virtual void endVisit(Block *) {} + + virtual bool visit(StatementList *) { return true; } + virtual void endVisit(StatementList *) {} + + virtual bool visit(VariableStatement *) { return true; } + virtual void endVisit(VariableStatement *) {} + + virtual bool visit(VariableDeclarationList *) { return true; } + virtual void endVisit(VariableDeclarationList *) {} + + virtual bool visit(VariableDeclaration *) { return true; } + virtual void endVisit(VariableDeclaration *) {} + + virtual bool visit(EmptyStatement *) { return true; } + virtual void endVisit(EmptyStatement *) {} + + virtual bool visit(ExpressionStatement *) { return true; } + virtual void endVisit(ExpressionStatement *) {} + + virtual bool visit(IfStatement *) { return true; } + virtual void endVisit(IfStatement *) {} + + virtual bool visit(DoWhileStatement *) { return true; } + virtual void endVisit(DoWhileStatement *) {} + + virtual bool visit(WhileStatement *) { return true; } + virtual void endVisit(WhileStatement *) {} + + virtual bool visit(ForStatement *) { return true; } + virtual void endVisit(ForStatement *) {} + + virtual bool visit(LocalForStatement *) { return true; } + virtual void endVisit(LocalForStatement *) {} + + virtual bool visit(ForEachStatement *) { return true; } + virtual void endVisit(ForEachStatement *) {} + + virtual bool visit(LocalForEachStatement *) { return true; } + virtual void endVisit(LocalForEachStatement *) {} + + virtual bool visit(ContinueStatement *) { return true; } + virtual void endVisit(ContinueStatement *) {} + + virtual bool visit(BreakStatement *) { return true; } + virtual void endVisit(BreakStatement *) {} + + virtual bool visit(ReturnStatement *) { return true; } + virtual void endVisit(ReturnStatement *) {} + + virtual bool visit(WithStatement *) { return true; } + virtual void endVisit(WithStatement *) {} + + virtual bool visit(SwitchStatement *) { return true; } + virtual void endVisit(SwitchStatement *) {} + + virtual bool visit(CaseBlock *) { return true; } + virtual void endVisit(CaseBlock *) {} + + virtual bool visit(CaseClauses *) { return true; } + virtual void endVisit(CaseClauses *) {} + + virtual bool visit(CaseClause *) { return true; } + virtual void endVisit(CaseClause *) {} + + virtual bool visit(DefaultClause *) { return true; } + virtual void endVisit(DefaultClause *) {} + + virtual bool visit(LabelledStatement *) { return true; } + virtual void endVisit(LabelledStatement *) {} + + virtual bool visit(ThrowStatement *) { return true; } + virtual void endVisit(ThrowStatement *) {} + + virtual bool visit(TryStatement *) { return true; } + virtual void endVisit(TryStatement *) {} + + virtual bool visit(Catch *) { return true; } + virtual void endVisit(Catch *) {} + + virtual bool visit(Finally *) { return true; } + virtual void endVisit(Finally *) {} + + virtual bool visit(FunctionDeclaration *) { return true; } + virtual void endVisit(FunctionDeclaration *) {} + + virtual bool visit(FunctionExpression *) { return true; } + virtual void endVisit(FunctionExpression *) {} + + virtual bool visit(FormalParameterList *) { return true; } + virtual void endVisit(FormalParameterList *) {} + + virtual bool visit(FunctionBody *) { return true; } + virtual void endVisit(FunctionBody *) {} + + virtual bool visit(Program *) { return true; } + virtual void endVisit(Program *) {} + + virtual bool visit(SourceElements *) { return true; } + virtual void endVisit(SourceElements *) {} + + virtual bool visit(FunctionSourceElement *) { return true; } + virtual void endVisit(FunctionSourceElement *) {} + + virtual bool visit(StatementSourceElement *) { return true; } + virtual void endVisit(StatementSourceElement *) {} + + virtual bool visit(DebuggerStatement *) { return true; } + virtual void endVisit(DebuggerStatement *) {} +}; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif // QQMLJSASTVISITOR_P_H diff --git a/src/qml/qml/parser/qqmljsengine_p.cpp b/src/qml/qml/parser/qqmljsengine_p.cpp new file mode 100644 index 0000000000..459ba8d7dc --- /dev/null +++ b/src/qml/qml/parser/qqmljsengine_p.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmljsengine_p.h" +#include "qqmljsglobal_p.h" + +#include <qnumeric.h> +#include <QHash> +#include <QDebug> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +static int toDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'z')) + return 10 + c - 'a'; + else if ((c >= 'A') && (c <= 'Z')) + return 10 + c - 'A'; + return -1; +} + +double integerFromString(const char *buf, int size, int radix) +{ + if (size == 0) + return qSNaN(); + + double sign = 1.0; + int i = 0; + if (buf[0] == '+') { + ++i; + } else if (buf[0] == '-') { + sign = -1.0; + ++i; + } + + if (((size-i) >= 2) && (buf[i] == '0')) { + if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) + && (radix < 34)) { + if ((radix != 0) && (radix != 16)) + return 0; + radix = 16; + i += 2; + } else { + if (radix == 0) { + radix = 8; + ++i; + } + } + } else if (radix == 0) { + radix = 10; + } + + int j = i; + for ( ; i < size; ++i) { + int d = toDigit(buf[i]); + if ((d == -1) || (d >= radix)) + break; + } + double result; + if (j == i) { + if (!qstrcmp(buf, "Infinity")) + result = qInf(); + else + result = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +double integerFromString(const QString &str, int radix) +{ + QByteArray ba = str.trimmed().toLatin1(); + return integerFromString(ba.constData(), ba.size(), radix); +} + + +Engine::Engine() + : _lexer(0) +{ } + +Engine::~Engine() +{ } + +void Engine::setCode(const QString &code) +{ _code = code; } + +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QQmlJS::AST::SourceLocation(pos, len, line, col)); } + +QList<QQmlJS::AST::SourceLocation> Engine::comments() const +{ return _comments; } + +Lexer *Engine::lexer() const +{ return _lexer; } + +void Engine::setLexer(Lexer *lexer) +{ _lexer = lexer; } + +MemoryPool *Engine::pool() +{ return &_pool; } + +QStringRef Engine::newStringRef(const QString &text) +{ + const int pos = _extraCode.length(); + _extraCode += text; + return _extraCode.midRef(pos, text.length()); +} + +QStringRef Engine::newStringRef(const QChar *chars, int size) +{ return newStringRef(QString(chars, size)); } + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsengine_p.h b/src/qml/qml/parser/qqmljsengine_p.h new file mode 100644 index 0000000000..3cb78de4eb --- /dev/null +++ b/src/qml/qml/parser/qqmljsengine_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSENGINE_P_H +#define QQMLJSENGINE_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 "qqmljsglobal_p.h" +#include "qqmljsastfwd_p.h" +#include "qqmljsmemorypool_p.h" + +#include <QString> +#include <QSet> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Lexer; +class MemoryPool; + +class QML_PARSER_EXPORT DiagnosticMessage +{ +public: + enum Kind { Warning, Error }; + + DiagnosticMessage() + : kind(Error) {} + + DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message) + : kind(kind), loc(loc), message(message) {} + + bool isWarning() const + { return kind == Warning; } + + bool isError() const + { return kind == Error; } + + Kind kind; + AST::SourceLocation loc; + QString message; +}; + +class QML_PARSER_EXPORT Engine +{ + Lexer *_lexer; + MemoryPool _pool; + QList<AST::SourceLocation> _comments; + QString _extraCode; + QString _code; + +public: + Engine(); + ~Engine(); + + void setCode(const QString &code); + + void addComment(int pos, int len, int line, int col); + QList<AST::SourceLocation> comments() const; + + Lexer *lexer() const; + void setLexer(Lexer *lexer); + + MemoryPool *pool(); + + inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); } + + QStringRef newStringRef(const QString &s); + QStringRef newStringRef(const QChar *chars, int size); +}; + +double integerFromString(const char *buf, int size, int radix); + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif // QQMLJSENGINE_P_H diff --git a/src/qml/qml/parser/qqmljsglobal_p.h b/src/qml/qml/parser/qqmljsglobal_p.h new file mode 100644 index 0000000000..81c90310ad --- /dev/null +++ b/src/qml/qml/parser/qqmljsglobal_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLJSGLOBAL_P_H +#define QQMLJSGLOBAL_P_H + +#include <QtCore/qglobal.h> + +#ifdef QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE + +# ifdef QDECLARATIVEJS_BUILD_DIR +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# elif QML_BUILD_STATIC_LIB +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif // QQMLJS_BUILD_DIR + +#else // !QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE QT_END_NAMESPACE +# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) + // QmlDevTools is a static library +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_AUTOTEST_EXPORT +# endif +#endif // QT_CREATOR + +#endif // QQMLJSGLOBAL_P_H diff --git a/src/qml/qml/parser/qqmljsgrammar.cpp b/src/qml/qml/parser/qqmljsgrammar.cpp new file mode 100644 index 0000000000..f69f809ee3 --- /dev/null +++ b/src/qml/qml/parser/qqmljsgrammar.cpp @@ -0,0 +1,1013 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file was generated by qlalr - DO NOT EDIT! +#include "qqmljsgrammar_p.h" + +QT_BEGIN_NAMESPACE + +const char *const QQmlJSGrammar::spell [] = { + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", + "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", + "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", "public", + "import", "as", "on", 0, 0, 0, 0, 0, 0, 0, + 0, 0}; + +const short QQmlJSGrammar::lhs [] = { + 102, 102, 102, 102, 102, 102, 103, 109, 109, 112, + 112, 114, 113, 113, 113, 113, 113, 113, 113, 113, + 116, 111, 110, 119, 119, 120, 120, 121, 121, 118, + 107, 107, 107, 107, 123, 123, 123, 123, 123, 123, + 123, 107, 131, 131, 131, 132, 132, 133, 133, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 117, 117, 117, 117, + 117, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 122, + 138, 138, 138, 138, 137, 137, 140, 140, 142, 142, + 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 144, 144, 115, 115, 115, + 115, 115, 147, 147, 148, 148, 148, 148, 146, 146, + 149, 149, 150, 150, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, + 153, 154, 154, 154, 155, 155, 155, 155, 156, 156, + 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, + 157, 158, 158, 158, 158, 158, 159, 159, 159, 159, + 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, + 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, + 169, 170, 170, 171, 171, 141, 141, 172, 172, 173, + 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, + 173, 105, 105, 174, 174, 175, 175, 176, 176, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 124, 185, 185, 184, 184, 135, + 135, 186, 186, 187, 187, 189, 189, 188, 190, 193, + 191, 191, 194, 192, 192, 125, 126, 126, 127, 127, + 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, + 178, 179, 179, 179, 179, 180, 180, 128, 129, 195, + 195, 198, 198, 196, 196, 199, 197, 181, 181, 181, + 182, 182, 130, 130, 130, 200, 201, 183, 183, 134, + 145, 205, 205, 202, 202, 203, 203, 206, 108, 108, + 207, 207, 106, 106, 204, 204, 139, 139, 208}; + +const short QQmlJSGrammar::rhs [] = { + 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 1, 2, 2, 3, 3, 5, 5, 4, 4, + 2, 0, 1, 1, 2, 1, 3, 2, 3, 2, + 1, 5, 4, 4, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 0, 1, 2, 4, 6, + 6, 3, 3, 7, 7, 4, 4, 5, 5, 5, + 6, 6, 10, 6, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 3, 3, 4, 5, 3, 4, 3, 1, + 1, 2, 3, 4, 1, 2, 3, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 3, 5, 1, 2, 4, 4, 4, 3, 0, 1, + 1, 3, 1, 1, 1, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 1, 3, 3, + 3, 1, 3, 3, 1, 3, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, + 3, 1, 3, 3, 3, 3, 1, 3, 3, 3, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 5, 1, 5, 1, 3, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 0, 1, 1, 3, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 2, 0, 1, 3, + 3, 1, 1, 1, 3, 1, 3, 2, 2, 2, + 0, 1, 2, 0, 1, 1, 2, 2, 7, 5, + 7, 7, 5, 9, 10, 7, 8, 2, 2, 3, + 3, 2, 2, 3, 3, 3, 3, 5, 5, 3, + 5, 1, 2, 0, 1, 4, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 5, 2, 2, 2, 8, + 8, 1, 3, 0, 1, 0, 1, 1, 1, 1, + 1, 2, 1, 1, 0, 1, 0, 1, 2}; + +const short QQmlJSGrammar::action_default [] = { + 0, 0, 22, 0, 0, 0, 22, 0, 175, 242, + 206, 214, 210, 154, 226, 202, 3, 139, 73, 155, + 218, 222, 143, 172, 153, 158, 138, 192, 179, 0, + 80, 81, 76, 345, 67, 347, 0, 0, 0, 0, + 78, 0, 0, 74, 77, 71, 0, 0, 68, 70, + 69, 79, 72, 0, 75, 0, 0, 168, 0, 0, + 155, 174, 157, 156, 0, 0, 0, 170, 171, 169, + 173, 0, 203, 0, 0, 0, 0, 193, 0, 0, + 0, 0, 0, 0, 183, 0, 0, 0, 177, 178, + 176, 181, 185, 184, 182, 180, 195, 194, 196, 0, + 211, 0, 207, 0, 0, 149, 136, 148, 137, 105, + 106, 107, 132, 108, 133, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 134, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 135, + 0, 0, 147, 243, 150, 0, 151, 0, 152, 146, + 0, 239, 232, 230, 237, 238, 236, 235, 241, 234, + 233, 231, 240, 227, 0, 215, 0, 0, 219, 0, + 0, 223, 0, 0, 149, 141, 0, 140, 0, 145, + 159, 0, 346, 334, 335, 0, 332, 0, 333, 0, + 336, 250, 257, 256, 264, 252, 0, 253, 337, 0, + 344, 254, 255, 260, 258, 341, 338, 343, 261, 0, + 272, 0, 0, 0, 0, 345, 67, 0, 347, 68, + 244, 286, 69, 0, 0, 0, 273, 0, 0, 262, + 263, 0, 251, 259, 287, 288, 331, 342, 0, 302, + 303, 304, 305, 0, 298, 299, 300, 301, 328, 329, + 0, 0, 0, 0, 0, 291, 292, 248, 246, 208, + 216, 212, 228, 204, 249, 0, 155, 220, 224, 197, + 186, 0, 0, 205, 0, 0, 0, 0, 198, 0, + 0, 0, 0, 0, 190, 188, 191, 189, 187, 200, + 199, 201, 0, 213, 0, 209, 0, 247, 155, 0, + 229, 244, 245, 0, 244, 0, 0, 294, 0, 0, + 0, 296, 0, 217, 0, 0, 221, 0, 0, 225, + 284, 0, 276, 285, 279, 0, 283, 0, 244, 277, + 0, 244, 0, 0, 295, 0, 0, 0, 297, 346, + 334, 0, 0, 336, 0, 330, 0, 320, 0, 0, + 0, 290, 0, 289, 0, 348, 0, 104, 266, 269, + 0, 105, 272, 108, 133, 110, 111, 76, 115, 116, + 67, 117, 120, 74, 77, 68, 244, 69, 79, 123, + 72, 125, 75, 127, 128, 273, 130, 131, 135, 0, + 97, 0, 0, 99, 103, 101, 88, 100, 102, 0, + 98, 87, 267, 265, 143, 144, 149, 0, 142, 0, + 319, 0, 306, 307, 0, 318, 0, 0, 0, 309, + 314, 312, 315, 0, 0, 313, 314, 0, 310, 0, + 311, 268, 317, 0, 268, 316, 0, 321, 322, 0, + 268, 323, 324, 0, 0, 325, 0, 0, 0, 326, + 327, 161, 160, 0, 0, 0, 293, 0, 0, 0, + 308, 281, 274, 0, 282, 278, 0, 280, 270, 0, + 271, 275, 91, 0, 0, 95, 82, 0, 84, 93, + 0, 85, 94, 96, 86, 92, 83, 0, 89, 165, + 163, 167, 164, 162, 166, 339, 6, 340, 4, 2, + 65, 90, 0, 0, 68, 70, 69, 31, 5, 0, + 66, 0, 45, 44, 43, 0, 0, 58, 0, 59, + 35, 36, 37, 38, 40, 41, 62, 39, 0, 45, + 0, 0, 0, 0, 0, 54, 0, 55, 0, 0, + 26, 0, 0, 63, 27, 0, 30, 28, 24, 0, + 29, 25, 0, 56, 0, 57, 143, 0, 60, 64, + 0, 0, 0, 0, 61, 0, 52, 46, 53, 47, + 0, 0, 0, 0, 49, 0, 50, 51, 48, 0, + 0, 143, 268, 0, 0, 42, 105, 272, 108, 133, + 110, 111, 76, 115, 116, 67, 117, 120, 74, 77, + 68, 244, 69, 79, 123, 72, 125, 75, 127, 128, + 273, 130, 131, 135, 0, 32, 33, 0, 34, 8, + 0, 10, 0, 9, 0, 1, 21, 12, 0, 13, + 0, 14, 0, 19, 20, 0, 15, 16, 0, 17, + 18, 11, 23, 7, 349}; + +const short QQmlJSGrammar::goto_default [] = { + 7, 625, 207, 196, 205, 508, 496, 624, 643, 495, + 623, 621, 626, 22, 622, 18, 507, 549, 539, 546, + 541, 526, 191, 195, 197, 201, 233, 208, 230, 530, + 570, 569, 200, 232, 26, 474, 473, 356, 355, 9, + 354, 357, 107, 17, 145, 24, 13, 144, 19, 25, + 57, 23, 8, 28, 27, 269, 15, 263, 10, 259, + 12, 261, 11, 260, 20, 267, 21, 268, 14, 262, + 258, 299, 411, 264, 265, 202, 193, 192, 204, 203, + 229, 194, 360, 359, 231, 463, 462, 321, 322, 465, + 324, 464, 323, 419, 423, 426, 422, 421, 441, 442, + 185, 199, 181, 184, 198, 206, 0}; + +const short QQmlJSGrammar::action_index [] = { + 404, 1275, 2411, 2411, 2509, 1000, 68, 92, 90, -102, + 88, 62, 60, 256, -102, 298, 86, -102, -102, 638, + 83, 134, 172, 219, -102, -102, -102, 454, 194, 1275, + -102, -102, -102, 381, -102, 2215, 1555, 1275, 1275, 1275, + -102, 790, 1275, -102, -102, -102, 1275, 1275, -102, -102, + -102, -102, -102, 1275, -102, 1275, 1275, -102, 1275, 1275, + 102, 217, -102, -102, 1275, 1275, 1275, -102, -102, -102, + 204, 1275, 304, 1275, 1275, 1275, 1275, 539, 1275, 1275, + 1275, 1275, 1275, 1275, 308, 1275, 1275, 1275, 103, 131, + 135, 308, 210, 225, 216, 308, 444, 390, 434, 1275, + 82, 1275, 100, 2117, 1275, 1275, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + 139, 1275, -102, -102, 91, 10, -102, 1275, -102, -102, + 1275, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, 1275, 26, 1275, 1275, 69, 66, + 1275, -102, 2117, 1275, 1275, -102, 97, -102, 44, -102, + -102, 67, -102, 297, 78, 24, -102, 291, -102, 36, + 2411, -102, -102, -102, -102, -102, 234, -102, -102, 12, + -102, -102, -102, -102, -102, -102, 2411, -102, -102, 464, + -102, 461, 115, 2509, 42, 381, 58, 46, 2705, 70, + 1275, -102, 74, 57, 1275, 65, -102, 59, 61, -102, + -102, 367, -102, -102, -102, -102, -102, -102, 106, -102, + -102, -102, -102, 87, -102, -102, -102, -102, -102, -102, + 56, 55, 1275, 99, 84, -102, -102, 1461, -102, 75, + 48, 52, -102, 306, 72, 53, 579, 77, 110, 370, + 230, 381, 1275, 286, 1275, 1275, 1275, 1275, 380, 1275, + 1275, 1275, 1275, 1275, 184, 169, 166, 190, 198, 460, + 363, 353, 1275, 50, 1275, 63, 1275, -102, 638, 1275, + -102, 1275, 64, 39, 1275, 30, 2509, -102, 1275, 173, + 2509, -102, 1275, 79, 1275, 1275, 81, 80, 1275, -102, + 71, 149, 32, -102, -102, 1275, -102, 381, 1275, -102, + 73, 1275, 76, 2509, -102, 1275, 142, 2509, -102, -16, + 381, -42, -12, 2411, -39, -102, 2509, -102, 1275, 154, + 2509, 14, 2509, -102, 20, 16, -32, -102, -102, 2509, + -51, 519, -4, 511, 136, 1275, 2509, -2, -35, 395, + -1, -27, 908, 4, 6, -102, 1370, -102, 0, -36, + 27, 1275, 47, 22, 1275, 45, 1275, 21, 17, 1275, + -102, 2313, 144, -102, -102, -102, -102, -102, -102, 1275, + -102, -102, -102, -102, 274, -102, 1275, -21, -102, 2509, + -102, 138, -102, -102, 2509, -102, 1275, 132, 5, -102, + 40, -102, 41, 101, 1275, -102, 38, 34, -102, -38, + -102, 2509, -102, 105, 2509, -102, 245, -102, -102, 96, + 2509, 11, -102, -7, -11, -102, 352, 8, 18, -102, + -102, -102, -102, 1275, 129, 2509, -102, 1275, 130, 2509, + -102, 49, -102, 226, -102, -102, 1275, -102, -102, 362, + -102, -102, -102, 107, 1837, -102, -102, 1649, -102, -102, + 1743, -102, -102, -102, -102, -102, -102, 114, -102, -102, + -102, -102, -102, -102, -102, -102, -102, 2411, -102, -102, + -102, 94, 9, 818, 189, -10, 31, -102, -102, 223, + -102, 191, -102, -102, -102, 300, 178, -102, 1928, -102, + -102, -102, -102, -102, -102, -102, -102, -102, 257, -25, + 381, 195, -22, 305, 240, -102, -6, -102, 818, 127, + -102, -18, 818, -102, -102, 1184, -102, -102, -102, 1092, + -102, -102, 237, -102, 1928, -102, 294, -8, -102, -102, + 176, 381, 19, 1928, -102, 165, -102, 174, -102, 2, + -52, 381, 183, 381, -102, 117, -102, -102, -102, 2019, + 880, 285, 2607, 1555, 3, -102, 522, 35, 453, 108, + 1275, 2509, 51, 23, 475, 54, -17, 700, 7, 43, + -102, 1370, -102, 28, -3, 33, 1275, 37, 15, 1275, + 25, 1275, 1, 13, 124, -102, -102, 29, -102, -102, + 728, -102, 250, -43, 627, -102, -102, 231, 372, -102, + 222, -102, 111, -102, -102, 381, -102, -102, 104, -102, + -102, -102, -102, -102, -102, + + -107, 9, -103, 2, 5, 266, 1, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -39, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 86, + -107, -107, -107, 8, -107, -107, -22, 19, 71, 174, + -107, 186, 171, -107, -107, -107, 184, 178, -107, -107, + -107, -107, -107, 144, -107, 124, 150, -107, 165, 161, + -107, -107, -107, -107, 156, 160, 157, -107, -107, -107, + -107, 147, -107, 142, 135, 179, 166, -107, 177, 170, + 117, 72, 134, 92, -107, 75, 94, 66, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 181, + -107, 106, -107, 143, 78, 55, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -5, -107, -107, -107, -107, -107, 54, -107, -107, + 51, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, 114, -107, 113, 38, -107, -107, + 41, -107, 231, 63, 112, -107, -107, -107, -107, -107, + -107, -107, -107, 30, -107, -107, -107, 52, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, 36, -107, -107, 45, + -107, 42, -107, 40, -107, 80, -107, -107, 77, -107, + 88, -107, -107, -107, 83, 74, -107, -107, -107, -107, + -107, -10, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, 23, -107, -107, -107, -107, 100, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, 4, 223, -107, 230, 236, 222, 205, -107, 127, + 125, 115, 96, 102, -107, -107, -107, -107, -107, -107, + -107, -107, 234, -107, 215, -107, 199, -107, -107, 197, + -107, 190, -107, -107, 163, -107, 90, -107, 0, -107, + -1, -107, 203, -107, 189, 211, -107, -107, 195, -107, + -107, -107, -107, -107, -107, 191, -107, 98, 119, -107, + -107, 95, -107, 81, -107, 79, -107, 82, -107, -107, + 101, -107, -107, -16, -107, -107, 53, -107, 46, -107, + 57, -107, 59, -107, -107, -107, -107, -107, -107, 35, + -107, 33, -107, 39, -107, 89, 67, -107, -107, 58, + -107, -107, 84, -107, -107, -107, 73, -107, -107, -107, + -107, 65, -107, 43, 93, -107, 109, -107, -107, 49, + -107, 47, -107, -107, -107, -107, -107, -107, -107, 50, + -107, -107, -107, -107, -107, -107, 108, -107, -107, 61, + -107, -107, -107, -107, 62, -107, 68, -107, -107, -107, + -107, -107, -23, -107, 69, -107, -19, -107, -107, -107, + -107, 97, -107, -107, 99, -107, -107, -107, -107, -107, + 60, -61, -107, -107, 34, -107, 37, -107, 29, -107, + -107, -107, -107, 32, -107, 76, -107, 44, -107, 56, + -107, -107, -107, -107, -107, -107, 31, -107, -107, 116, + -107, -107, -107, -107, -6, -107, -107, 70, -107, -107, + 64, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, 193, -107, -107, + -107, -107, -107, 7, -107, -107, -107, -107, -107, -107, + -107, -20, -107, -107, -107, -7, -107, -107, 290, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -2, -25, -107, -15, -107, -107, -107, -107, 172, -107, + -107, -107, 287, -107, -107, 288, -107, -107, -107, 291, + -107, -107, -107, -107, 336, -107, -107, 20, -107, -107, + 15, 3, -107, 304, -107, -107, -107, 24, -107, -107, + -107, 28, 21, 26, -107, -107, -107, -107, -107, 320, + 104, -107, 13, 381, -3, -107, 6, -107, 10, -107, + 167, 22, -107, -107, 12, -107, -107, 87, -107, -107, + -107, 25, -107, -107, -107, -107, 11, -107, 14, 85, + -107, 121, -107, -107, -107, -107, -107, 27, -107, -107, + 17, -107, -107, 18, 91, -107, -107, -107, 16, -107, + -107, -107, -107, -107, -107, -4, -107, -107, -107, -107, + -107, -107, -107, -107, -107}; + +const short QQmlJSGrammar::action_info [] = { + 416, 257, 533, -132, 403, -113, 346, -102, 575, 348, + 572, -121, 531, -103, -121, 545, 345, 430, 342, 348, + 340, 343, 440, 401, 391, 545, 563, 389, 538, 446, + 352, 444, -129, 416, -124, -102, 545, 453, 420, 408, + -124, 431, -132, 424, -126, 424, 424, 620, 440, 457, + -103, 440, -129, 457, -126, 440, 560, 453, -113, 257, + 565, 346, 545, 335, 272, 346, 466, 236, 448, 190, + 149, 164, 141, 170, 99, 511, 272, 409, 257, 312, + 296, 414, 348, 312, 189, 164, 187, 318, 325, 71, + 306, 252, 644, 416, 141, 453, 292, 457, 440, 147, + 304, 71, 443, 183, 179, 141, 0, 141, 0, 172, + 99, 427, 434, 141, 301, 477, 444, 0, 0, 0, + 0, 0, 141, 0, 0, 0, 0, 292, 173, 294, + 58, 294, 542, 251, 331, 542, 333, 141, 141, 101, + 141, 59, 0, 58, 62, 256, 255, 141, 247, 246, + 141, 399, 0, 177, 59, 63, 428, 327, 620, 254, + 314, 101, 141, 478, 315, 640, 639, 242, 241, 249, + 248, 58, 634, 633, 488, 58, 249, 248, 577, 576, + 615, 141, 59, 543, 166, 518, 59, 172, 167, 455, + 459, 85, 418, 86, 85, 142, 86, 249, 248, 413, + 412, 567, 337, 512, 87, 512, 173, 87, 174, 85, + 328, 86, 512, 0, 350, 85, 64, 86, 529, 85, + 512, 86, 87, 85, 512, 86, 568, 566, 87, 64, + 579, 64, 87, 310, 469, 85, 87, 86, 0, 519, + 517, 85, 141, 86, 554, 0, 172, 536, 87, 514, + 85, 514, 86, 141, 87, 85, 545, 86, 514, 0, + 513, 65, 513, 87, 514, 173, 514, 66, 87, 513, + 514, 103, 172, 0, 65, 513, 65, 513, 0, 0, + 66, 513, 66, 637, 636, 0, 0, 470, 468, 172, + 104, 173, 105, 406, 0, 235, 234, 630, 555, 553, + 172, 537, 535, 0, 274, 275, 438, 437, 173, 172, + 406, 631, 629, 635, 0, 580, 73, 74, -90, 173, + 34, 174, 73, 74, 274, 275, 34, -90, 173, 34, + 174, 276, 277, 85, 34, 86, 0, 0, 0, 0, + 0, 628, 0, 75, 76, 0, 87, 0, 0, 75, + 76, 276, 277, 0, 0, 0, 0, 48, 50, 49, + 0, 0, 0, 48, 50, 49, 48, 50, 49, 0, + 0, 48, 50, 49, 0, 0, 279, 280, 0, 0, + 0, 34, 0, 45, 0, 281, 279, 280, 282, 45, + 283, 34, 45, 279, 280, 281, 34, 45, 282, 0, + 283, 34, 281, 279, 280, 282, 0, 283, 0, 0, + 34, 0, 281, 78, 79, 282, 0, 283, 48, 50, + 49, 80, 81, 0, 34, 82, 0, 83, 48, 50, + 49, -345, 0, 48, 50, 49, 0, 0, 48, 50, + 49, 0, 0, 0, 45, 0, 0, 48, 50, 49, + 0, 0, 0, 0, 45, 0, 0, 78, 79, 45, + 0, 48, 50, 49, 45, 80, 81, 78, 79, 82, + 0, 83, 0, 45, 0, 80, 81, 78, 79, 82, + 0, 83, 34, 279, 280, 80, 81, 45, 0, 82, + 34, 83, 281, 34, 0, 282, 0, 283, 6, 5, + 4, 1, 3, 2, 34, 0, 0, 0, 0, 0, + 0, -345, 0, 0, 245, 244, 0, 0, 0, 48, + 50, 49, 245, 244, 0, 240, 239, 48, 50, 49, + 48, 50, 49, 0, 0, 0, 0, 0, 0, 0, + 34, 48, 50, 49, 0, 45, 0, 0, 34, 0, + 0, 34, 0, 45, 0, 0, 45, 0, 0, 0, + 0, 0, 78, 79, 0, 0, 0, 45, 0, 0, + 80, 81, 245, 244, 82, 0, 83, 48, 50, 49, + 240, 239, 151, 240, 239, 48, 50, 49, 48, 50, + 49, 0, 152, 0, 0, 0, 153, 0, 0, 0, + 0, 0, 0, 45, 0, 154, 0, 155, 0, 0, + 308, 45, 0, 0, 45, 0, 0, 0, 156, 0, + 157, 62, 0, 0, 0, 0, 0, 0, 158, 0, + 0, 159, 63, 0, 0, 0, 0, 160, 0, 30, + 31, 151, 0, 161, 0, 0, 0, 0, 0, 33, + 0, 152, 0, 0, 0, 153, 34, 0, 0, 162, + 35, 36, 0, 37, 154, 0, 155, 0, 0, 0, + 503, 0, 0, 0, 44, 0, 0, 156, 0, 157, + 62, 0, 0, 0, 0, 0, 0, 158, 0, 0, + 159, 63, 51, 48, 50, 49, 160, 52, 0, 0, + 0, 0, 161, 0, 0, 0, 0, 0, 43, 54, + 32, 0, 30, 31, 40, 0, 0, 0, 162, 45, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 41, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 503, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 43, + 54, 32, 33, 0, 0, 40, 0, 0, 0, 34, + 45, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 41, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 503, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 43, + 54, 32, 33, 0, 0, 40, 0, 0, 0, 34, + 45, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 503, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 41, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 502, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 215, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 0, 0, 0, 503, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 51, 504, 506, 505, 0, + 52, 0, 0, 0, 0, 226, 0, 0, 0, 0, + 0, 43, 54, 32, 210, 0, 0, 40, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 502, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 35, 36, 0, 37, 0, + 0, 0, 0, 0, 0, 503, 0, 0, 0, 44, + 0, 0, 0, 0, 0, 0, 0, 550, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 504, 506, + 505, 0, 52, 0, 0, 0, 0, 226, 0, 0, + 0, 0, 0, 43, 54, 32, 210, 0, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 502, 0, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, + 37, 0, 0, 0, 0, 0, 0, 503, 0, 0, + 0, 44, 0, 0, 0, 0, 0, 0, 0, 547, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 504, 506, 505, 0, 52, 0, 0, 0, 0, 226, + 0, 0, 0, 0, 0, 43, 54, 32, 210, 0, + 0, 40, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -122, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 38, 0, 39, 41, 42, 0, 0, 44, 0, 0, + 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 51, 48, 50, 49, 0, + 52, 0, 53, 0, 55, 0, 56, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 53, 0, 55, 271, 56, 0, 0, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 475, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 476, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 475, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 0, 35, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 481, 0, 0, 0, 0, + 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, + 0, 53, 0, 55, 0, 56, 0, 0, 0, 0, + 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 483, 0, 0, 29, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 34, 0, 0, 0, 35, 36, 0, 37, + 0, 0, 0, 38, 0, 39, 41, 42, 0, 0, + 44, 0, 0, 0, 46, 0, 47, 0, 0, 484, + 0, 0, 0, 0, 0, 0, 0, 0, 51, 48, + 50, 49, 0, 52, 0, 53, 0, 55, 0, 56, + 0, 0, 0, 0, 43, 54, 32, 0, 0, 0, + 40, 0, 0, 0, 0, 45, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 483, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, + 35, 36, 0, 37, 0, 0, 0, 38, 0, 39, + 41, 42, 0, 0, 44, 0, 0, 0, 46, 0, + 47, 0, 0, 486, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 48, 50, 49, 0, 52, 0, 53, + 0, 55, 0, 56, 0, 0, 0, 0, 43, 54, + 32, 0, 0, 0, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 217, 0, + 0, 218, 36, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 0, 0, 0, 0, 0, 221, + 0, 0, 0, 51, 48, 50, 49, 223, 52, 0, + 53, 225, 55, 0, 56, 0, 228, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 217, + 0, 0, 582, 583, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, + 221, 0, 0, 0, 51, 48, 50, 49, 223, 52, + 0, 53, 225, 55, 0, 56, 0, 228, 0, 0, + 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 109, 110, 111, 0, 0, 113, 115, 116, 0, + 0, 117, 0, 118, 0, 0, 0, 120, 121, 122, + 0, 0, 0, 0, 0, 0, 34, 123, 124, 125, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 129, 0, 0, 0, + 0, 0, 0, 48, 50, 49, 130, 131, 132, 0, + 134, 135, 136, 137, 138, 139, 0, 0, 127, 133, + 119, 112, 114, 128, 0, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, + 110, 111, 0, 0, 113, 115, 116, 0, 0, 117, + 0, 118, 0, 0, 0, 120, 121, 122, 0, 0, + 0, 0, 0, 0, 393, 123, 124, 125, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, + 0, 0, 394, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 0, 0, 0, 0, 0, + 398, 395, 397, 0, 130, 131, 132, 0, 134, 135, + 136, 137, 138, 139, 0, 0, 127, 133, 119, 112, + 114, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 109, 110, 111, + 0, 0, 113, 115, 116, 0, 0, 117, 0, 118, + 0, 0, 0, 120, 121, 122, 0, 0, 0, 0, + 0, 0, 393, 123, 124, 125, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, + 394, 0, 0, 0, 0, 0, 0, 0, 396, 0, + 0, 0, 129, 0, 0, 0, 0, 0, 398, 395, + 397, 0, 130, 131, 132, 0, 134, 135, 136, 137, + 138, 139, 0, 0, 127, 133, 119, 112, 114, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, + 211, 0, 29, 30, 31, 213, 0, 0, 0, 0, + 0, 0, 214, 215, 0, 0, 0, 0, 0, 0, + 216, 217, 0, 0, 218, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, + 220, 0, 221, 0, 0, 0, 51, 219, 222, 49, + 223, 52, 224, 53, 225, 55, 226, 56, 227, 228, + 0, 0, 43, 54, 32, 210, 212, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 209, 0, 0, 0, 0, 211, 0, + 29, 30, 31, 213, 0, 0, 0, 0, 0, 0, + 214, 33, 0, 0, 0, 0, 0, 0, 216, 217, + 0, 0, 218, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 220, 0, + 221, 0, 0, 0, 51, 219, 222, 49, 223, 52, + 224, 53, 225, 55, 226, 56, 227, 228, 0, 0, + 43, 54, 32, 210, 212, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 586, 110, 111, 0, 0, 588, 115, 590, 30, + 31, 591, 0, 118, 0, 0, 0, 120, 593, 594, + 0, 0, 0, 0, 0, 0, 595, 596, 124, 125, + 218, 36, 0, 37, 0, 0, 0, 38, 0, 39, + 597, 42, 0, 0, 599, 0, 0, 0, 46, 0, + 47, 0, 0, 0, 0, 0, 601, 0, 221, 0, + 0, 0, 603, 600, 602, 49, 604, 605, 606, 53, + 608, 609, 610, 611, 612, 613, 0, 0, 598, 607, + 592, 587, 589, 128, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 361, + 110, 111, 0, 0, 363, 115, 365, 30, 31, 366, + 0, 118, 0, 0, 0, 120, 368, 369, 0, 0, + 0, 0, 0, 0, 370, 371, 124, 125, 218, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 372, 42, + 0, 0, 374, 0, 0, 0, 46, 0, 47, 0, + -268, 0, 0, 0, 376, 0, 221, 0, 0, 0, + 378, 375, 377, 49, 379, 380, 381, 53, 383, 384, + 385, 386, 387, 388, 0, 0, 373, 382, 367, 362, + 364, 128, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + + 534, 311, 497, 309, 532, 461, 498, 499, 516, 515, + 619, 638, 16, 552, 436, 358, 616, 472, 562, 320, + 528, 238, 487, 182, 250, 243, 253, 182, 302, 641, + 627, 632, 150, 485, 143, 454, 439, 402, 445, 559, + 237, 574, 250, 578, 561, 186, 618, 458, 238, 349, + 573, 449, 447, 571, 243, 347, 450, 243, 460, 351, + 238, 353, 358, 410, 415, 439, 176, 188, 436, 250, + 467, 417, 433, 182, 425, 429, 302, 169, 456, 358, + 171, 140, 336, 334, 338, 344, 436, 392, 390, 400, + 163, 302, 307, 148, 146, 339, 439, 404, 302, 358, + 404, 358, 0, 482, 501, 480, 0, 642, 0, 479, + 0, 0, 0, 320, 60, 0, 186, 501, 90, 60, + 60, 489, 302, 60, 617, 93, 0, 88, 0, 405, + 0, 461, 405, 60, 60, 451, 180, 60, 0, 180, + 60, 60, 60, 451, 60, 95, 89, 146, 266, 287, + 60, 146, 407, 270, 60, 288, 178, 60, 106, 452, + 0, 60, 60, 60, 102, 60, 302, 332, 286, 60, + 92, 452, 60, 60, 451, 60, 165, 168, 285, 432, + 284, 435, 60, 60, 108, 501, 329, 94, 540, 96, + 60, 330, 60, 302, 494, 60, 77, 237, 60, 404, + 452, 341, 471, 72, 60, 60, 67, 69, 60, 60, + 68, 0, 70, 60, 60, 60, 61, 180, 60, 60, + 98, 491, 60, 91, 490, 60, 60, 60, 493, 60, + 84, 405, 60, 97, 492, 305, 0, 60, 0, 298, + 0, 100, 270, 298, 270, 298, 106, 298, 270, 0, + 270, 60, 270, 60, 316, 0, 270, 0, 270, 298, + 291, 326, 303, 60, 270, 319, 313, 300, 270, 297, + 60, 60, 108, 175, 295, 270, 270, 290, 60, 501, + 273, 317, 60, 270, 60, 278, 509, 270, 0, 270, + 0, 289, 0, 548, 0, 293, 551, 0, 500, 510, + 501, 501, 0, 544, 501, 0, 0, 0, 509, 0, + 0, 509, 520, 521, 522, 523, 527, 524, 525, 0, + 500, 510, 0, 500, 510, 564, 520, 521, 522, 523, + 527, 524, 525, 581, 0, 0, 0, 0, 0, 0, + 584, 585, 520, 521, 522, 523, 527, 524, 525, 556, + 0, 0, 0, 0, 0, 0, 557, 558, 520, 521, + 522, 523, 527, 524, 525, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 556, 0, 0, 540, 0, 614, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 472, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +const short QQmlJSGrammar::action_check [] = { + 36, 36, 24, 7, 55, 7, 7, 7, 60, 36, + 8, 7, 37, 7, 7, 33, 55, 55, 60, 36, + 36, 33, 33, 55, 8, 33, 7, 7, 34, 36, + 16, 20, 7, 36, 7, 7, 33, 36, 33, 60, + 7, 7, 7, 5, 7, 5, 5, 90, 33, 36, + 7, 33, 7, 36, 7, 33, 66, 36, 7, 36, + 29, 7, 33, 31, 1, 7, 17, 55, 60, 33, + 60, 2, 8, 7, 48, 66, 1, 7, 36, 2, + 8, 7, 36, 2, 60, 2, 8, 7, 17, 1, + 60, 36, 0, 36, 8, 36, 48, 36, 33, 8, + 61, 1, 6, 36, 60, 8, -1, 8, -1, 15, + 48, 10, 7, 8, 61, 8, 20, -1, -1, -1, + -1, -1, 8, -1, -1, -1, -1, 48, 34, 79, + 40, 79, 8, 77, 61, 8, 60, 8, 8, 79, + 8, 51, -1, 40, 42, 61, 62, 8, 61, 62, + 8, 7, -1, 56, 51, 53, 55, 8, 90, 60, + 50, 79, 8, 56, 54, 61, 62, 61, 62, 61, + 62, 40, 61, 62, 60, 40, 61, 62, 61, 62, + 56, 8, 51, 56, 50, 7, 51, 15, 54, 60, + 60, 25, 60, 27, 25, 56, 27, 61, 62, 61, + 62, 36, 60, 29, 38, 29, 34, 38, 36, 25, + 61, 27, 29, -1, 60, 25, 12, 27, 29, 25, + 29, 27, 38, 25, 29, 27, 61, 62, 38, 12, + 7, 12, 38, 60, 8, 25, 38, 27, -1, 61, + 62, 25, 8, 27, 7, -1, 15, 7, 38, 75, + 25, 75, 27, 8, 38, 25, 33, 27, 75, -1, + 86, 57, 86, 38, 75, 34, 75, 63, 38, 86, + 75, 15, 15, -1, 57, 86, 57, 86, -1, -1, + 63, 86, 63, 61, 62, -1, -1, 61, 62, 15, + 34, 34, 36, 36, -1, 61, 62, 47, 61, 62, + 15, 61, 62, -1, 18, 19, 61, 62, 34, 15, + 36, 61, 62, 91, -1, 92, 18, 19, 33, 34, + 29, 36, 18, 19, 18, 19, 29, 33, 34, 29, + 36, 45, 46, 25, 29, 27, -1, -1, -1, -1, + -1, 91, -1, 45, 46, -1, 38, -1, -1, 45, + 46, 45, 46, -1, -1, -1, -1, 66, 67, 68, + -1, -1, -1, 66, 67, 68, 66, 67, 68, -1, + -1, 66, 67, 68, -1, -1, 23, 24, -1, -1, + -1, 29, -1, 92, -1, 32, 23, 24, 35, 92, + 37, 29, 92, 23, 24, 32, 29, 92, 35, -1, + 37, 29, 32, 23, 24, 35, -1, 37, -1, -1, + 29, -1, 32, 23, 24, 35, -1, 37, 66, 67, + 68, 31, 32, -1, 29, 35, -1, 37, 66, 67, + 68, 36, -1, 66, 67, 68, -1, -1, 66, 67, + 68, -1, -1, -1, 92, -1, -1, 66, 67, 68, + -1, -1, -1, -1, 92, -1, -1, 23, 24, 92, + -1, 66, 67, 68, 92, 31, 32, 23, 24, 35, + -1, 37, -1, 92, -1, 31, 32, 23, 24, 35, + -1, 37, 29, 23, 24, 31, 32, 92, -1, 35, + 29, 37, 32, 29, -1, 35, -1, 37, 94, 95, + 96, 97, 98, 99, 29, -1, -1, -1, -1, -1, + -1, 36, -1, -1, 61, 62, -1, -1, -1, 66, + 67, 68, 61, 62, -1, 61, 62, 66, 67, 68, + 66, 67, 68, -1, -1, -1, -1, -1, -1, -1, + 29, 66, 67, 68, -1, 92, -1, -1, 29, -1, + -1, 29, -1, 92, -1, -1, 92, -1, -1, -1, + -1, -1, 23, 24, -1, -1, -1, 92, -1, -1, + 31, 32, 61, 62, 35, -1, 37, 66, 67, 68, + 61, 62, 3, 61, 62, 66, 67, 68, 66, 67, + 68, -1, 13, -1, -1, -1, 17, -1, -1, -1, + -1, -1, -1, 92, -1, 26, -1, 28, -1, -1, + 31, 92, -1, -1, 92, -1, -1, -1, 39, -1, + 41, 42, -1, -1, -1, -1, -1, -1, 49, -1, + -1, 52, 53, -1, -1, -1, -1, 58, -1, 12, + 13, 3, -1, 64, -1, -1, -1, -1, -1, 22, + -1, 13, -1, -1, -1, 17, 29, -1, -1, 80, + 33, 34, -1, 36, 26, -1, 28, -1, -1, -1, + 43, -1, -1, -1, 47, -1, -1, 39, -1, 41, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + 52, 53, 65, 66, 67, 68, 58, 70, -1, -1, + -1, -1, 64, -1, -1, -1, -1, -1, 81, 82, + 83, -1, 12, 13, 87, -1, -1, -1, 80, 92, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, 81, + 82, 83, 22, -1, -1, 87, -1, -1, -1, 29, + 92, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, 81, + 82, 83, 22, -1, -1, 87, -1, -1, -1, 29, + 92, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, + -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, + -1, -1, 92, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, + -1, -1, -1, -1, -1, -1, -1, 55, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, + -1, -1, -1, 81, 82, 83, 84, -1, -1, 87, + -1, -1, -1, -1, 92, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 10, -1, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, -1, -1, -1, 75, + -1, -1, -1, -1, -1, 81, 82, 83, 84, -1, + -1, 87, -1, -1, -1, -1, 92, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, 72, -1, 74, 75, 76, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, 56, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, 56, + -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, + 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, + -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, + 87, -1, -1, -1, -1, 92, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, + -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, + -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, 61, + -1, -1, -1, 65, 66, 67, 68, 69, 70, -1, + 72, 73, 74, -1, 76, -1, 78, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, 30, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, -1, 76, -1, 78, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, + -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 59, -1, -1, -1, + -1, -1, -1, 66, 67, 68, 69, 70, 71, -1, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, -1, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + 65, 66, 67, -1, 69, 70, 71, -1, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, + -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, + -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, + 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, + -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, + 67, -1, 69, 70, 71, -1, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, + 9, -1, 11, 12, 13, 14, -1, -1, -1, -1, + -1, -1, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, -1, -1, -1, -1, 9, -1, + 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 5, 6, -1, -1, 9, 10, 11, 12, + 13, 14, -1, 16, -1, -1, -1, 20, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, 87, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, 12, 13, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + 55, -1, -1, -1, 59, -1, 61, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, + + 15, 2, 105, 3, 29, 15, 4, 2, 15, 29, + 9, 15, 3, 15, 3, 2, 19, 39, 15, 15, + 13, 15, 3, 15, 2, 15, 3, 15, 3, 11, + 13, 15, 71, 39, 39, 3, 22, 2, 99, 19, + 4, 15, 2, 15, 29, 15, 19, 3, 15, 3, + 29, 22, 15, 29, 15, 2, 22, 15, 2, 2, + 15, 2, 2, 2, 2, 22, 3, 15, 3, 2, + 39, 3, 3, 15, 97, 94, 3, 39, 2, 2, + 39, 3, 3, 2, 2, 101, 3, 40, 39, 39, + 39, 3, 2, 39, 39, 15, 22, 13, 3, 2, + 13, 2, -1, 39, 13, 35, -1, 16, -1, 39, + -1, -1, -1, 15, 48, -1, 15, 13, 52, 48, + 48, 50, 3, 48, 20, 53, -1, 52, -1, 45, + -1, 15, 45, 48, 48, 50, 50, 48, -1, 50, + 48, 48, 48, 50, 48, 53, 52, 39, 48, 53, + 48, 39, 44, 53, 48, 53, 44, 48, 15, 50, + -1, 48, 48, 48, 58, 48, 3, 72, 53, 48, + 53, 50, 48, 48, 50, 48, 62, 64, 53, 82, + 53, 82, 48, 48, 41, 13, 88, 53, 16, 54, + 48, 72, 48, 3, 50, 48, 54, 4, 48, 13, + 50, 100, 86, 56, 48, 48, 50, 50, 48, 48, + 50, -1, 51, 48, 48, 48, 51, 50, 48, 48, + 54, 50, 48, 53, 50, 48, 48, 48, 50, 48, + 53, 45, 48, 54, 50, 72, -1, 48, -1, 48, + -1, 60, 53, 48, 53, 48, 15, 48, 53, -1, + 53, 48, 53, 48, 65, -1, 53, -1, 53, 48, + 55, 70, 72, 48, 53, 70, 63, 70, 53, 70, + 48, 48, 41, 42, 59, 53, 53, 55, 48, 13, + 57, 70, 48, 53, 48, 55, 20, 53, -1, 53, + -1, 55, -1, 5, -1, 61, 5, -1, 32, 33, + 13, 13, -1, 16, 13, -1, -1, -1, 20, -1, + -1, 20, 22, 23, 24, 25, 26, 27, 28, -1, + 32, 33, -1, 32, 33, 21, 22, 23, 24, 25, + 26, 27, 28, 13, -1, -1, -1, -1, -1, -1, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 13, + -1, -1, -1, -1, -1, -1, 20, 21, 22, 23, + 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 13, -1, -1, 16, -1, 18, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}; + +QT_END_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsgrammar_p.h b/src/qml/qml/parser/qqmljsgrammar_p.h new file mode 100644 index 0000000000..455391a862 --- /dev/null +++ b/src/qml/qml/parser/qqmljsgrammar_p.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QQMLJSGRAMMAR_P_H +#define QQMLJSGRAMMAR_P_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QQmlJSGrammar +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + REDUCE_HERE = 101, + SHIFT_THERE = 100, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AS = 91, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 88, + T_CONST = 84, + T_CONTINUE = 9, + T_DEBUGGER = 85, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_ERROR = 93, + T_FALSE = 83, + T_FEED_JS_EXPRESSION = 97, + T_FEED_JS_PROGRAM = 99, + T_FEED_JS_SOURCE_ELEMENT = 98, + T_FEED_JS_STATEMENT = 96, + T_FEED_UI_OBJECT_MEMBER = 95, + T_FEED_UI_PROGRAM = 94, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 90, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 87, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 81, + T_NUMERIC_LITERAL = 47, + T_ON = 92, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PROPERTY = 66, + T_PUBLIC = 89, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 68, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 86, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SIGNAL = 67, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 69, + T_THIS = 70, + T_THROW = 71, + T_TILDE = 72, + T_TRUE = 82, + T_TRY = 73, + T_TYPEOF = 74, + T_VAR = 75, + T_VOID = 76, + T_WHILE = 77, + T_WITH = 78, + T_XOR = 79, + T_XOR_EQ = 80, + + ACCEPT_STATE = 644, + RULE_COUNT = 349, + STATE_COUNT = 645, + TERMINAL_COUNT = 102, + NON_TERMINAL_COUNT = 107, + + GOTO_INDEX_OFFSET = 645, + GOTO_INFO_OFFSET = 2807, + GOTO_CHECK_OFFSET = 2807 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +QT_END_NAMESPACE +#endif // QQMLJSGRAMMAR_P_H + diff --git a/src/qml/qml/parser/qqmljskeywords_p.h b/src/qml/qml/parser/qqmljskeywords_p.h new file mode 100644 index 0000000000..bbcc4855a3 --- /dev/null +++ b/src/qml/qml/parser/qqmljskeywords_p.h @@ -0,0 +1,860 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSKEYWORDS_P_H +#define QQMLJSKEYWORDS_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. +// + +static inline int classify2(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'a') { + if (s[1].unicode() == 's') { + return qmlMode ? Lexer::T_AS : Lexer::T_RESERVED_WORD; + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'o') { + return Lexer::T_DO; + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'f') { + return Lexer::T_IF; + } + else if (s[1].unicode() == 'n') { + return Lexer::T_IN; + } + } + else if (qmlMode && s[0].unicode() == 'o') { + if (s[1].unicode() == 'n') { + return Lexer::T_ON; + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify3(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'r') { + return Lexer::T_FOR; + } + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'n') { + if (s[2].unicode() == 't') { + return Lexer::T_INT; + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'w') { + return Lexer::T_NEW; + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'y') { + return Lexer::T_TRY; + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'r') { + return Lexer::T_VAR; + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify4(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'y') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + return Lexer::T_BYTE; + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 'e') { + return Lexer::T_CASE; + } + } + } + else if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'r') { + return Lexer::T_CHAR; + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'l') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 'e') { + return Lexer::T_ELSE; + } + } + } + else if (s[1].unicode() == 'n') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'm') { + return Lexer::T_ENUM; + } + } + } + } + else if (s[0].unicode() == 'g') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'o') { + return Lexer::T_GOTO; + } + } + } + } + else if (s[0].unicode() == 'l') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'g') { + return Lexer::T_LONG; + } + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'l') { + return Lexer::T_NULL; + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 's') { + return Lexer::T_THIS; + } + } + } + else if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'e') { + return Lexer::T_TRUE; + } + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'd') { + return Lexer::T_VOID; + } + } + } + } + else if (s[0].unicode() == 'w') { + if (s[1].unicode() == 'i') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'h') { + return Lexer::T_WITH; + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify5(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'e') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'k') { + return Lexer::T_BREAK; + } + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 'h') { + return Lexer::T_CATCH; + } + } + } + } + else if (s[1].unicode() == 'l') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 's') { + return Lexer::T_CLASS; + } + } + } + } + else if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 't') { + return Lexer::T_CONST; + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 'e') { + return Lexer::T_FALSE; + } + } + } + } + else if (s[1].unicode() == 'i') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'l') { + return Lexer::T_FINAL; + } + } + } + } + else if (s[1].unicode() == 'l') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 't') { + return Lexer::T_FLOAT; + } + } + } + } + } + else if (s[0].unicode() == 's') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'r') { + if (s[4].unicode() == 't') { + return Lexer::T_SHORT; + } + } + } + } + else if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'r') { + return Lexer::T_SUPER; + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'r') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'w') { + return Lexer::T_THROW; + } + } + } + } + } + else if (s[0].unicode() == 'w') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + return Lexer::T_WHILE; + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify6(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'e') { + return Lexer::T_DELETE; + } + } + } + } + } + else if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'b') { + if (s[4].unicode() == 'l') { + if (s[5].unicode() == 'e') { + return Lexer::T_DOUBLE; + } + } + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'x') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 't') { + return Lexer::T_EXPORT; + } + } + } + } + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'm') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 't') { + return qmlMode ? Lexer::T_IMPORT : Lexer::T_RESERVED_WORD; + } + } + } + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'i') { + if (s[4].unicode() == 'v') { + if (s[5].unicode() == 'e') { + return Lexer::T_NATIVE; + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'b') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'c') { + return qmlMode ? Lexer::T_PUBLIC : Lexer::T_RESERVED_WORD; + } + } + } + } + } + } + else if (s[0].unicode() == 'r') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'u') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'n') { + return Lexer::T_RETURN; + } + } + } + } + } + } + else if (s[0].unicode() == 's') { + if (qmlMode && s[1].unicode() == 'i') { + if (s[2].unicode() == 'g') { + if (s[3].unicode() == 'n') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'l') { + return Lexer::T_SIGNAL; + } + } + } + } + } + else if (s[1].unicode() == 't') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'c') { + return Lexer::T_STATIC; + } + } + } + } + } + else if (s[1].unicode() == 'w') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'c') { + if (s[5].unicode() == 'h') { + return Lexer::T_SWITCH; + } + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'r') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'w') { + if (s[5].unicode() == 's') { + return Lexer::T_THROWS; + } + } + } + } + } + else if (s[1].unicode() == 'y') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'o') { + if (s[5].unicode() == 'f') { + return Lexer::T_TYPEOF; + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify7(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'a') { + if (s[6].unicode() == 'n') { + return Lexer::T_BOOLEAN; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'f') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'u') { + if (s[5].unicode() == 'l') { + if (s[6].unicode() == 't') { + return Lexer::T_DEFAULT; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'x') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'n') { + if (s[5].unicode() == 'd') { + if (s[6].unicode() == 's') { + return Lexer::T_EXTENDS; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'i') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'l') { + if (s[5].unicode() == 'l') { + if (s[6].unicode() == 'y') { + return Lexer::T_FINALLY; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'c') { + if (s[3].unicode() == 'k') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'g') { + if (s[6].unicode() == 'e') { + return Lexer::T_PACKAGE; + } + } + } + } + } + } + else if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'v') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 't') { + if (s[6].unicode() == 'e') { + return Lexer::T_PRIVATE; + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify8(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'a') { + if (s[1].unicode() == 'b') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'a') { + if (s[6].unicode() == 'c') { + if (s[7].unicode() == 't') { + return Lexer::T_ABSTRACT; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'u') { + if (s[7].unicode() == 'e') { + return Lexer::T_CONTINUE; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'b') { + if (s[3].unicode() == 'u') { + if (s[4].unicode() == 'g') { + if (s[5].unicode() == 'g') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'r') { + return Lexer::T_DEBUGGER; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'o') { + if (s[7].unicode() == 'n') { + return Lexer::T_FUNCTION; + } + } + } + } + } + } + } + } + else if (qmlMode && s[0].unicode() == 'p') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'p') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'r') { + if (s[6].unicode() == 't') { + if (s[7].unicode() == 'y') { + return Lexer::T_PROPERTY; + } + } + } + } + } + } + } + } + else if (qmlMode && s[0].unicode() == 'r') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'd') { + if (s[4].unicode() == 'o') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'l') { + if (s[7].unicode() == 'y') { + return Lexer::T_READONLY; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'l') { + if (s[7].unicode() == 'e') { + return Lexer::T_VOLATILE; + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify9(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'n') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'f') { + if (s[6].unicode() == 'a') { + if (s[7].unicode() == 'c') { + if (s[8].unicode() == 'e') { + return Lexer::T_INTERFACE; + } + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'c') { + if (s[6].unicode() == 't') { + if (s[7].unicode() == 'e') { + if (s[8].unicode() == 'd') { + return Lexer::T_PROTECTED; + } + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'n') { + if (s[4].unicode() == 's') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 't') { + return Lexer::T_TRANSIENT; + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify10(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'm') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'm') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 't') { + if (s[9].unicode() == 's') { + return Lexer::T_IMPLEMENTS; + } + } + } + } + } + } + } + } + } + else if (s[1].unicode() == 'n') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'c') { + if (s[7].unicode() == 'e') { + if (s[8].unicode() == 'o') { + if (s[9].unicode() == 'f') { + return Lexer::T_INSTANCEOF; + } + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify12(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 's') { + if (s[1].unicode() == 'y') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 'h') { + if (s[5].unicode() == 'r') { + if (s[6].unicode() == 'o') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 'i') { + if (s[9].unicode() == 'z') { + if (s[10].unicode() == 'e') { + if (s[11].unicode() == 'd') { + return Lexer::T_SYNCHRONIZED; + } + } + } + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +int Lexer::classify(const QChar *s, int n, bool qmlMode) { + switch (n) { + case 2: return classify2(s, qmlMode); + case 3: return classify3(s, qmlMode); + case 4: return classify4(s, qmlMode); + case 5: return classify5(s, qmlMode); + case 6: return classify6(s, qmlMode); + case 7: return classify7(s, qmlMode); + case 8: return classify8(s, qmlMode); + case 9: return classify9(s, qmlMode); + case 10: return classify10(s, qmlMode); + case 12: return classify12(s, qmlMode); + default: return Lexer::T_IDENTIFIER; + } // switch +} + +#endif // QQMLJSKEYWORDS_P_H diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp new file mode 100644 index 0000000000..c34fc81163 --- /dev/null +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -0,0 +1,1166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmljslexer_p.h" +#include "qqmljsengine_p.h" +#include "qqmljsmemorypool_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QVarLengthArray> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); +QT_END_NAMESPACE + +using namespace QQmlJS; + +static int regExpFlagFromChar(const QChar &ch) +{ + switch (ch.unicode()) { + case 'g': return Lexer::RegExp_Global; + case 'i': return Lexer::RegExp_IgnoreCase; + case 'm': return Lexer::RegExp_Multiline; + } + return 0; +} + +static unsigned char convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +static QChar convertHex(QChar c1, QChar c2) +{ + return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); +} + +static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) +{ + return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()), + (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); +} + +Lexer::Lexer(Engine *engine) + : _engine(engine) + , _codePtr(0) + , _lastLinePtr(0) + , _tokenLinePtr(0) + , _tokenStartPtr(0) + , _char(QLatin1Char('\n')) + , _errorCode(NoError) + , _currentLineNumber(0) + , _tokenValue(0) + , _parenthesesState(IgnoreParentheses) + , _parenthesesCount(0) + , _stackToken(-1) + , _patternFlags(0) + , _tokenKind(0) + , _tokenLength(0) + , _tokenLine(0) + , _validTokenText(false) + , _prohibitAutomaticSemicolon(false) + , _restrictedKeyword(false) + , _terminator(false) + , _followsClosingBrace(false) + , _delimited(true) + , _qmlMode(true) +{ + if (engine) + engine->setLexer(this); +} + +bool Lexer::qmlMode() const +{ + return _qmlMode; +} + +QString Lexer::code() const +{ + return _code; +} + +void Lexer::setCode(const QString &code, int lineno, bool qmlMode) +{ + if (_engine) + _engine->setCode(code); + + _qmlMode = qmlMode; + _code = code; + _tokenText.clear(); + _tokenText.reserve(1024); + _errorMessage.clear(); + _tokenSpell = QStringRef(); + + _codePtr = code.unicode(); + _lastLinePtr = _codePtr; + _tokenLinePtr = _codePtr; + _tokenStartPtr = _codePtr; + + _char = QLatin1Char('\n'); + _errorCode = NoError; + + _currentLineNumber = lineno; + _tokenValue = 0; + + // parentheses state + _parenthesesState = IgnoreParentheses; + _parenthesesCount = 0; + + _stackToken = -1; + + _patternFlags = 0; + _tokenLength = 0; + _tokenLine = lineno; + + _validTokenText = false; + _prohibitAutomaticSemicolon = false; + _restrictedKeyword = false; + _terminator = false; + _followsClosingBrace = false; + _delimited = true; +} + +void Lexer::scanChar() +{ + _char = *_codePtr++; + + if (_char == QLatin1Char('\n')) { + _lastLinePtr = _codePtr; // points to the first character after the newline + ++_currentLineNumber; + } +} + +int Lexer::lex() +{ + const int previousTokenKind = _tokenKind; + + _tokenSpell = QStringRef(); + _tokenKind = scanToken(); + _tokenLength = _codePtr - _tokenStartPtr - 1; + + _delimited = false; + _restrictedKeyword = false; + _followsClosingBrace = (previousTokenKind == T_RBRACE); + + // update the flags + switch (_tokenKind) { + case T_LBRACE: + case T_SEMICOLON: + case T_COLON: + _delimited = true; + break; + + case T_IF: + case T_FOR: + case T_WHILE: + case T_WITH: + _parenthesesState = CountParentheses; + _parenthesesCount = 0; + break; + + case T_DO: + _parenthesesState = BalancedParentheses; + break; + + case T_CONTINUE: + case T_BREAK: + case T_RETURN: + case T_THROW: + _restrictedKeyword = true; + break; + } // switch + + // update the parentheses state + switch (_parenthesesState) { + case IgnoreParentheses: + break; + + case CountParentheses: + if (_tokenKind == T_RPAREN) { + --_parenthesesCount; + if (_parenthesesCount == 0) + _parenthesesState = BalancedParentheses; + } else if (_tokenKind == T_LPAREN) { + ++_parenthesesCount; + } + break; + + case BalancedParentheses: + _parenthesesState = IgnoreParentheses; + break; + } // switch + + return _tokenKind; +} + +bool Lexer::isUnicodeEscapeSequence(const QChar *chars) +{ + if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3])) + return true; + + return false; +} + +QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) +{ + if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) { + scanChar(); // skip u + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + const QChar c3 = _char; + scanChar(); + + const QChar c4 = _char; + scanChar(); + + if (ok) + *ok = true; + + return convertUnicode(c1, c2, c3, c4); + } + + *ok = false; + return QChar(); +} + +int Lexer::scanToken() +{ + if (_stackToken != -1) { + int tk = _stackToken; + _stackToken = -1; + return tk; + } + + _terminator = false; + +again: + _validTokenText = false; + _tokenLinePtr = _lastLinePtr; + + while (_char.isSpace()) { + if (_char == QLatin1Char('\n')) { + _tokenLinePtr = _codePtr; + + if (_restrictedKeyword) { + // automatic semicolon insertion + _tokenLine = _currentLineNumber; + _tokenStartPtr = _codePtr - 1; // ### TODO: insert it before the optional \r sequence. + return T_SEMICOLON; + } else { + _terminator = true; + syncProhibitAutomaticSemicolon(); + } + } + + scanChar(); + } + + _tokenStartPtr = _codePtr - 1; + _tokenLine = _currentLineNumber; + + if (_char.isNull()) + return EOF_SYMBOL; + + const QChar ch = _char; + scanChar(); + + switch (ch.unicode()) { + case '~': return T_TILDE; + case '}': return T_RBRACE; + + case '|': + if (_char == QLatin1Char('|')) { + scanChar(); + return T_OR_OR; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_OR_EQ; + } + return T_OR; + + case '{': return T_LBRACE; + + case '^': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_XOR_EQ; + } + return T_XOR; + + case ']': return T_RBRACKET; + case '[': return T_LBRACKET; + case '?': return T_QUESTION; + + case '>': + if (_char == QLatin1Char('>')) { + scanChar(); + if (_char == QLatin1Char('>')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_GT_GT_GT_EQ; + } + return T_GT_GT_GT; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_GT_GT_EQ; + } + return T_GT_GT; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_GE; + } + return T_GT; + + case '=': + if (_char == QLatin1Char('=')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_EQ_EQ_EQ; + } + return T_EQ_EQ; + } + return T_EQ; + + case '<': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_LE; + } else if (_char == QLatin1Char('<')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_LT_LT_EQ; + } + return T_LT_LT; + } + return T_LT; + + case ';': return T_SEMICOLON; + case ':': return T_COLON; + + case '/': + if (_char == QLatin1Char('*')) { + scanChar(); + while (!_char.isNull()) { + if (_char == QLatin1Char('*')) { + scanChar(); + if (_char == QLatin1Char('/')) { + scanChar(); + + if (_engine) { + _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4, + tokenStartLine(), tokenStartColumn() + 2); + } + + goto again; + } + } else { + scanChar(); + } + } + } else if (_char == QLatin1Char('/')) { + while (!_char.isNull() && _char != QLatin1Char('\n')) { + scanChar(); + } + if (_engine) { + _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2, + tokenStartLine(), tokenStartColumn() + 2); + } + goto again; + } if (_char == QLatin1Char('=')) { + scanChar(); + return T_DIVIDE_EQ; + } + return T_DIVIDE_; + + case '.': + if (_char.isDigit()) { + QVarLengthArray<char,32> chars; + + chars.append(ch.unicode()); // append the `.' + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + + chars.append('\0'); + + const char *begin = chars.constData(); + const char *end = 0; + bool ok = false; + + _tokenValue = qstrtod(begin, &end, &ok); + + if (end - begin != chars.size() - 1) { + _errorCode = IllegalExponentIndicator; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); + return T_ERROR; + } + + return T_NUMERIC_LITERAL; + } + return T_DOT; + + case '-': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_MINUS_EQ; + } else if (_char == QLatin1Char('-')) { + scanChar(); + + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + _stackToken = T_MINUS_MINUS; + return T_SEMICOLON; + } + + return T_MINUS_MINUS; + } + return T_MINUS; + + case ',': return T_COMMA; + + case '+': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_PLUS_EQ; + } else if (_char == QLatin1Char('+')) { + scanChar(); + + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + _stackToken = T_PLUS_PLUS; + return T_SEMICOLON; + } + + return T_PLUS_PLUS; + } + return T_PLUS; + + case '*': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_STAR_EQ; + } + return T_STAR; + + case ')': return T_RPAREN; + case '(': return T_LPAREN; + + case '&': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_AND_EQ; + } else if (_char == QLatin1Char('&')) { + scanChar(); + return T_AND_AND; + } + return T_AND; + + case '%': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_REMAINDER_EQ; + } + return T_REMAINDER; + + case '!': + if (_char == QLatin1Char('=')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_NOT_EQ_EQ; + } + return T_NOT_EQ; + } + return T_NOT; + + case '\'': + case '"': { + const QChar quote = ch; + bool multilineStringLiteral = false; + + const QChar *startCode = _codePtr; + + if (_engine) { + while (!_char.isNull()) { + if (_char == QLatin1Char('\n') || _char == QLatin1Char('\\')) { + break; + } else if (_char == quote) { + _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); + scanChar(); + + return T_STRING_LITERAL; + } + scanChar(); + } + } + + _validTokenText = true; + _tokenText.resize(0); + startCode--; + while (startCode != _codePtr - 1) + _tokenText += *startCode++; + + while (! _char.isNull()) { + if (_char == QLatin1Char('\n')) { + multilineStringLiteral = true; + _tokenText += _char; + scanChar(); + } else if (_char == quote) { + scanChar(); + + if (_engine) + _tokenSpell = _engine->newStringRef(_tokenText); + + return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; + } else if (_char == QLatin1Char('\\')) { + scanChar(); + + QChar u; + bool ok = false; + + switch (_char.unicode()) { + // unicode escape sequence + case 'u': + u = decodeUnicodeEscapeCharacter(&ok); + if (! ok) + u = _char; + break; + + // hex escape sequence + case 'x': + case 'X': + if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { + scanChar(); + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + u = convertHex(c1, c2); + } else { + u = _char; + } + break; + + // single character escape sequence + case '\\': u = QLatin1Char('\\'); scanChar(); break; + case '\'': u = QLatin1Char('\''); scanChar(); break; + case '\"': u = QLatin1Char('\"'); scanChar(); break; + case 'b': u = QLatin1Char('\b'); scanChar(); break; + case 'f': u = QLatin1Char('\f'); scanChar(); break; + case 'n': u = QLatin1Char('\n'); scanChar(); break; + case 'r': u = QLatin1Char('\r'); scanChar(); break; + case 't': u = QLatin1Char('\t'); scanChar(); break; + case 'v': u = QLatin1Char('\v'); scanChar(); break; + + case '0': + if (! _codePtr[1].isDigit()) { + scanChar(); + u = QLatin1Char('\0'); + } else { + // ### parse deprecated octal escape sequence ? + u = _char; + } + break; + + case '\r': + while (_char == QLatin1Char('\r')) + scanChar(); + + if (_char == QLatin1Char('\n')) { + u = _char; + scanChar(); + } else { + u = QLatin1Char('\n'); + } + + break; + + case '\n': + u = _char; + scanChar(); + break; + + default: + // non escape character + u = _char; + scanChar(); + } + + _tokenText += u; + } else { + _tokenText += _char; + scanChar(); + } + } + + _errorCode = UnclosedStringLiteral; + _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line"); + return T_ERROR; + } + + default: + if (ch.isLetter() || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) { + bool identifierWithEscapeChars = false; + if (ch == QLatin1Char('\\')) { + identifierWithEscapeChars = true; + _tokenText.resize(0); + bool ok = false; + _tokenText += decodeUnicodeEscapeCharacter(&ok); + _validTokenText = true; + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } + } + while (true) { + if (_char.isLetterOrNumber() || _char == QLatin1Char('$') || _char == QLatin1Char('_')) { + if (identifierWithEscapeChars) + _tokenText += _char; + + scanChar(); + } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { + if (! identifierWithEscapeChars) { + identifierWithEscapeChars = true; + _tokenText.resize(0); + _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1); + _validTokenText = true; + } + + scanChar(); // skip '\\' + bool ok = false; + _tokenText += decodeUnicodeEscapeCharacter(&ok); + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } + } else { + _tokenLength = _codePtr - _tokenStartPtr - 1; + + int kind = T_IDENTIFIER; + + if (! identifierWithEscapeChars) + kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); + + if (_engine) { + if (kind == T_IDENTIFIER && identifierWithEscapeChars) + _tokenSpell = _engine->newStringRef(_tokenText); + else + _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); + } + + return kind; + } + } + } else if (ch.isDigit()) { + if (ch != QLatin1Char('0')) { + double integer = ch.unicode() - '0'; + + QChar n = _char; + const QChar *code = _codePtr; + while (n.isDigit()) { + integer = integer * 10 + (n.unicode() - '0'); + n = *code++; + } + + if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) { + if (code != _codePtr) { + _codePtr = code - 1; + scanChar(); + } + _tokenValue = integer; + return T_NUMERIC_LITERAL; + } + } + + QVarLengthArray<char,32> chars; + chars.append(ch.unicode()); + + if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) { + // parse hex integer literal + + chars.append(_char.unicode()); + scanChar(); // consume `x' + + while (isHexDigit(_char)) { + chars.append(_char.unicode()); + scanChar(); + } + + _tokenValue = integerFromString(chars.constData(), chars.size(), 16); + return T_NUMERIC_LITERAL; + } + + // decimal integer literal + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); // consume the digit + } + + if (_char == QLatin1Char('.')) { + chars.append(_char.unicode()); + scanChar(); // consume `.' + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + + chars.append('\0'); + + const char *begin = chars.constData(); + const char *end = 0; + bool ok = false; + + _tokenValue = qstrtod(begin, &end, &ok); + + if (end - begin != chars.size() - 1) { + _errorCode = IllegalExponentIndicator; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); + return T_ERROR; + } + + return T_NUMERIC_LITERAL; + } + + break; + } + + return T_ERROR; +} + +bool Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + _tokenText.resize(0); + _validTokenText = true; + _patternFlags = 0; + + if (prefix == EqualPrefix) + _tokenText += QLatin1Char('='); + + while (true) { + switch (_char.unicode()) { + case 0: // eof + case '\n': case '\r': // line terminator + _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression literal"); + return false; + + case '/': + scanChar(); + + // scan the flags + _patternFlags = 0; + while (isIdentLetter(_char)) { + int flag = regExpFlagFromChar(_char); + if (flag == 0) { + _errorMessage = QCoreApplication::translate("QQmlParser", "Invalid regular expression flag '%0'") + .arg(QChar(_char)); + return false; + } + _patternFlags |= flag; + scanChar(); + } + + _tokenLength = _codePtr - _tokenStartPtr - 1; + return true; + + case '\\': + // regular expression backslash sequence + _tokenText += _char; + scanChar(); + + if (_char.isNull() || isLineTerminator()) { + _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + _tokenText += _char; + scanChar(); + break; + + case '[': + // regular expression class + _tokenText += _char; + scanChar(); + + while (! _char.isNull() && ! isLineTerminator()) { + if (_char == QLatin1Char(']')) + break; + else if (_char == QLatin1Char('\\')) { + // regular expression backslash sequence + _tokenText += _char; + scanChar(); + + if (_char.isNull() || isLineTerminator()) { + _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + _tokenText += _char; + scanChar(); + } else { + _tokenText += _char; + scanChar(); + } + } + + if (_char != QLatin1Char(']')) { + _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression class"); + return false; + } + + _tokenText += _char; + scanChar(); // skip ] + break; + + default: + _tokenText += _char; + scanChar(); + } // switch + } // while + + return false; +} + +bool Lexer::isLineTerminator() const +{ + return (_char == QLatin1Char('\n') || _char == QLatin1Char('\r')); +} + +bool Lexer::isIdentLetter(QChar ch) +{ + // ASCII-biased, since all reserved words are ASCII, aand hence the + // bulk of content to be parsed. + if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) + || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z')) + || ch == QLatin1Char('$') + || ch == QLatin1Char('_')) + return true; + if (ch.unicode() < 128) + return false; + return ch.isLetterOrNumber(); +} + +bool Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool Lexer::isHexDigit(QChar c) +{ + return ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) + || (c >= QLatin1Char('a') && c <= QLatin1Char('f')) + || (c >= QLatin1Char('A') && c <= QLatin1Char('F'))); +} + +bool Lexer::isOctalDigit(ushort c) +{ + return (c >= '0' && c <= '7'); +} + +int Lexer::tokenKind() const +{ + return _tokenKind; +} + +int Lexer::tokenOffset() const +{ + return _tokenStartPtr - _code.unicode(); +} + +int Lexer::tokenLength() const +{ + return _tokenLength; +} + +int Lexer::tokenStartLine() const +{ + return _tokenLine; +} + +int Lexer::tokenStartColumn() const +{ + return _tokenStartPtr - _tokenLinePtr + 1; +} + +int Lexer::tokenEndLine() const +{ + return _currentLineNumber; +} + +int Lexer::tokenEndColumn() const +{ + return _codePtr - _lastLinePtr; +} + +QStringRef Lexer::tokenSpell() const +{ + return _tokenSpell; +} + +double Lexer::tokenValue() const +{ + return _tokenValue; +} + +QString Lexer::tokenText() const +{ + if (_validTokenText) + return _tokenText; + + if (_tokenKind == T_STRING_LITERAL) + return QString(_tokenStartPtr + 1, _tokenLength - 2); + + return QString(_tokenStartPtr, _tokenLength); +} + +Lexer::Error Lexer::errorCode() const +{ + return _errorCode; +} + +QString Lexer::errorMessage() const +{ + return _errorMessage; +} + +void Lexer::syncProhibitAutomaticSemicolon() +{ + if (_parenthesesState == BalancedParentheses) { + // we have seen something like "if (foo)", which means we should + // never insert an automatic semicolon at this point, since it would + // then be expanded into an empty statement (ECMA-262 7.9.1) + _prohibitAutomaticSemicolon = true; + _parenthesesState = IgnoreParentheses; + } else { + _prohibitAutomaticSemicolon = false; + } +} + +bool Lexer::prevTerminator() const +{ + return _terminator; +} + +bool Lexer::followsClosingBrace() const +{ + return _followsClosingBrace; +} + +bool Lexer::canInsertAutomaticSemicolon(int token) const +{ + return token == T_RBRACE + || token == EOF_SYMBOL + || _terminator + || _followsClosingBrace; +} + +bool Lexer::scanDirectives(Directives *directives) +{ + if (_qmlMode) { + // the directives are a Javascript-only extension. + return false; + } + + lex(); // fetch the first token + + if (_tokenKind != T_DOT) + return true; + + do { + lex(); // skip T_DOT + + const int lineNumber = tokenStartLine(); + + if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD)) + return false; // expected a valid QML/JS directive + + const QString directiveName = tokenText(); + + if (! (directiveName == QLatin1String("pragma") || + directiveName == QLatin1String("import"))) + return false; // not a valid directive name + + // it must be a pragma or an import directive. + if (directiveName == QLatin1String("pragma")) { + // .pragma library + if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) + return false; // expected `library + + // we found a .pragma library directive + directives->pragmaLibrary(); + + } else { + Q_ASSERT(directiveName == QLatin1String("import")); + lex(); // skip .import + + QString pathOrUri; + QString version; + bool fileImport = false; // file or uri import + + if (_tokenKind == T_STRING_LITERAL) { + // .import T_STRING_LITERAL as T_IDENTIFIER + + fileImport = true; + pathOrUri = tokenText(); + + } else if (_tokenKind == T_IDENTIFIER) { + // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER + + pathOrUri = tokenText(); + + lex(); // skip the first T_IDENTIFIER + for (; _tokenKind == T_DOT; lex()) { + if (lex() != T_IDENTIFIER) + return false; + + pathOrUri += QLatin1Char('.'); + pathOrUri += tokenText(); + } + + if (_tokenKind != T_NUMERIC_LITERAL) + return false; // expected the module version number + + version = tokenText(); + } + + // + // recognize the mandatory `as' followed by the module name + // + if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as"))) + return false; // expected `as' + + if (lex() != T_IDENTIFIER) + return false; // expected module name + + const QString module = tokenText(); + + if (fileImport) + directives->importFile(pathOrUri, module); + else + directives->importModule(pathOrUri, version, module); + } + + if (tokenStartLine() != lineNumber) + return false; // the directives cannot span over multiple lines + + // fetch the first token after the .pragma/.import directive + lex(); + } while (_tokenKind == T_DOT); + + return true; +} + +#include "qqmljskeywords_p.h" diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h new file mode 100644 index 0000000000..6b51852f5f --- /dev/null +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSLEXER_P_H +#define QQMLJSLEXER_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 "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Directives { +public: + virtual ~Directives() {} + + virtual void pragmaLibrary() + { + } + + virtual void importFile(const QString &jsfile, const QString &module) + { + Q_UNUSED(jsfile); + Q_UNUSED(module); + } + + virtual void importModule(const QString &uri, const QString &version, const QString &module) + { + Q_UNUSED(uri); + Q_UNUSED(version); + Q_UNUSED(module); + } +}; + +class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar +{ +public: + enum { + T_ABSTRACT = T_RESERVED_WORD, + T_BOOLEAN = T_RESERVED_WORD, + T_BYTE = T_RESERVED_WORD, + T_CHAR = T_RESERVED_WORD, + T_CLASS = T_RESERVED_WORD, + T_DOUBLE = T_RESERVED_WORD, + T_ENUM = T_RESERVED_WORD, + T_EXPORT = T_RESERVED_WORD, + T_EXTENDS = T_RESERVED_WORD, + T_FINAL = T_RESERVED_WORD, + T_FLOAT = T_RESERVED_WORD, + T_GOTO = T_RESERVED_WORD, + T_IMPLEMENTS = T_RESERVED_WORD, + T_INT = T_RESERVED_WORD, + T_INTERFACE = T_RESERVED_WORD, + T_LET = T_RESERVED_WORD, + T_LONG = T_RESERVED_WORD, + T_NATIVE = T_RESERVED_WORD, + T_PACKAGE = T_RESERVED_WORD, + T_PRIVATE = T_RESERVED_WORD, + T_PROTECTED = T_RESERVED_WORD, + T_SHORT = T_RESERVED_WORD, + T_STATIC = T_RESERVED_WORD, + T_SUPER = T_RESERVED_WORD, + T_SYNCHRONIZED = T_RESERVED_WORD, + T_THROWS = T_RESERVED_WORD, + T_TRANSIENT = T_RESERVED_WORD, + T_VOLATILE = T_RESERVED_WORD, + T_YIELD = T_RESERVED_WORD + }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + enum RegExpFlag { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + +public: + Lexer(Engine *engine); + + bool qmlMode() const; + + QString code() const; + void setCode(const QString &code, int lineno, bool qmlMode = true); + + int lex(); + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + bool scanDirectives(Directives *directives); + + int regExpFlags() const { return _patternFlags; } + QString regExpPattern() const { return _tokenText; } + + int tokenKind() const; + int tokenOffset() const; + int tokenLength() const; + + int tokenStartLine() const; + int tokenStartColumn() const; + + int tokenEndLine() const; + int tokenEndColumn() const; + + QStringRef tokenSpell() const; + double tokenValue() const; + QString tokenText() const; + + Error errorCode() const; + QString errorMessage() const; + + bool prevTerminator() const; + bool followsClosingBrace() const; + bool canInsertAutomaticSemicolon(int token) const; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + +protected: + int classify(const QChar *s, int n, bool qmlMode); + +private: + inline void scanChar(); + int scanToken(); + + bool isLineTerminator() const; + static bool isIdentLetter(QChar c); + static bool isDecimalDigit(ushort c); + static bool isHexDigit(QChar c); + static bool isOctalDigit(ushort c); + static bool isUnicodeEscapeSequence(const QChar *chars); + + void syncProhibitAutomaticSemicolon(); + QChar decodeUnicodeEscapeCharacter(bool *ok); + +private: + Engine *_engine; + + QString _code; + QString _tokenText; + QString _errorMessage; + QStringRef _tokenSpell; + + const QChar *_codePtr; + const QChar *_lastLinePtr; + const QChar *_tokenLinePtr; + const QChar *_tokenStartPtr; + + QChar _char; + Error _errorCode; + + int _currentLineNumber; + double _tokenValue; + + // parentheses state + ParenthesesState _parenthesesState; + int _parenthesesCount; + + int _stackToken; + + int _patternFlags; + int _tokenKind; + int _tokenLength; + int _tokenLine; + + bool _validTokenText; + bool _prohibitAutomaticSemicolon; + bool _restrictedKeyword; + bool _terminator; + bool _followsClosingBrace; + bool _delimited; + bool _qmlMode; +}; + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif // LEXER_H diff --git a/src/qml/qml/parser/qqmljsmemorypool_p.h b/src/qml/qml/parser/qqmljsmemorypool_p.h new file mode 100644 index 0000000000..fd52fd25e4 --- /dev/null +++ b/src/qml/qml/parser/qqmljsmemorypool_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSMEMORYPOOL_P_H +#define QQMLJSMEMORYPOOL_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 "qqmljsglobal_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qdebug.h> + +#include <cstring> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class QML_PARSER_EXPORT MemoryPool : public QSharedData +{ + MemoryPool(const MemoryPool &other); + void operator =(const MemoryPool &other); + +public: + MemoryPool() + : _blocks(0), + _allocatedBlocks(0), + _blockCount(-1), + _ptr(0), + _end(0) + { } + + ~MemoryPool() + { + if (_blocks) { + for (int i = 0; i < _allocatedBlocks; ++i) { + if (char *b = _blocks[i]) + qFree(b); + } + + qFree(_blocks); + } + } + + inline void *allocate(size_t size) + { + size = (size + 7) & ~7; + if (_ptr && (_ptr + size < _end)) { + void *addr = _ptr; + _ptr += size; + return addr; + } + return allocate_helper(size); + } + + void reset() + { + _blockCount = -1; + _ptr = _end = 0; + } + +private: + void *allocate_helper(size_t size) + { + Q_ASSERT(size < BLOCK_SIZE); + + if (++_blockCount == _allocatedBlocks) { + if (! _allocatedBlocks) + _allocatedBlocks = DEFAULT_BLOCK_COUNT; + else + _allocatedBlocks *= 2; + + _blocks = (char **) qRealloc(_blocks, sizeof(char *) * _allocatedBlocks); + + for (int index = _blockCount; index < _allocatedBlocks; ++index) + _blocks[index] = 0; + } + + char *&block = _blocks[_blockCount]; + + if (! block) + block = (char *) qMalloc(BLOCK_SIZE); + + _ptr = block; + _end = _ptr + BLOCK_SIZE; + + void *addr = _ptr; + _ptr += size; + return addr; + } + +private: + char **_blocks; + int _allocatedBlocks; + int _blockCount; + char *_ptr; + char *_end; + + enum + { + BLOCK_SIZE = 8 * 1024, + DEFAULT_BLOCK_COUNT = 8 + }; +}; + +class QML_PARSER_EXPORT Managed +{ + Managed(const Managed &other); + void operator = (const Managed &other); + +public: + Managed() {} + ~Managed() {} + + void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); } + void operator delete(void *) {} + void operator delete(void *, MemoryPool *) {} +}; + +} // namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/qml/qml/parser/qqmljsparser.cpp b/src/qml/qml/parser/qqmljsparser.cpp new file mode 100644 index 0000000000..bc89b2ac84 --- /dev/null +++ b/src/qml/qml/parser/qqmljsparser.cpp @@ -0,0 +1,1812 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QtDebug> +#include <QtCore/QCoreApplication> + +#include <string.h> + +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsast_p.h" +#include "qqmljsmemorypool_p.h" + + + +#include "qqmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QQmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef))); +} + +Parser::Parser(Engine *engine): + driver(engine), + pool(engine->pool()), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + string_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + free(sym_stack); + free(state_stack); + free(location_stack); + free(string_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->tokenStartLine(); + loc.startColumn = lexer->tokenStartColumn(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<QStringRef, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->tokenValue(); + yytokenspell = lexer->tokenSpell(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yytokenspell = first_token->spell; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + stringRef(1) = yytokenspell; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { + +case 0: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 1: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 2: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 3: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 4: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 5: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 6: { + sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; + +case 8: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; + +case 9: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport); +} break; + +case 10: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport); +} break; + +case 13: { + sym(1).UiImport->semicolonToken = loc(2); +} break; + +case 15: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; + +case 17: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = stringRef(4); + sym(1).UiImport->semicolonToken = loc(5); +} break; + +case 19: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = stringRef(3); + sym(1).UiImport->semicolonToken = loc(4); +} break; + +case 20: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = new (pool) AST::UiImport(importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = new (pool) AST::UiImport(qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; + +case 21: { + sym(1).Node = 0; +} break; + +case 22: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; + +case 23: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; + +case 24: { + AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; + +case 25: { + sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); +} break; + +case 26: { + AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 27: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; + +case 28: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 29: { + AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; + +case 31: { + AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 32: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 33: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; + +case 41: +{ + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 45: { + sym(1).Node = 0; +} break; + +case 46: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; + +case 47: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2)); + node->propertyTypeToken = loc(1); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; + +case 48: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4)); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; + +case 50: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; + +case 52: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 54: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 56: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; + +case 58: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; + +case 59: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3), + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 60: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 61: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 62: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 63: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 64: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; + +case 65: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; + +case 71: { + AST::ThisExpression *node = new (pool) AST::ThisExpression(); + node->thisToken = loc(1); + sym(1).Node = node; +} break; + +case 72: { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 73: { + AST::NullExpression *node = new (pool) AST::NullExpression(); + node->nullToken = loc(1); + sym(1).Node = node; +} break; + +case 74: { + AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); + node->trueToken = loc(1); + sym(1).Node = node; +} break; + +case 75: { + AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); + node->falseToken = loc(1); + sym(1).Node = node; +} break; + +case 76: { + AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +case 77: +case 78: { + AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 79: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 80: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 81: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; + +case 82: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 83: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 84: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 85: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 86: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + else + node = new (pool) AST::ObjectLiteral(); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 87: { + AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; + +case 88: { + AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; + +case 89: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; + +case 90: { + sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); +} break; + +case 91: { + sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); +} break; + +case 92: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 93: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 94: { + AST::Elision *node = new (pool) AST::Elision(); + node->commaToken = loc(1); + sym(1).Node = node; +} break; + +case 95: { + AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 96: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 97: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 98: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +case 99: +case 100: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 101: { + AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 102: { + AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 103: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 139: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 140: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 141: { + AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; + +case 143: { + AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; + +case 144: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 145: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 146: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 147: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 148: { + sym(1).Node = 0; +} break; + +case 149: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; + +case 150: { + sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); +} break; + +case 151: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 155: { + AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; + +case 156: { + AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; + +case 158: { + AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; + +case 159: { + AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; + +case 160: { + AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; + +case 161: { + AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; + +case 162: { + AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; + +case 163: { + AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; + +case 164: { + AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; + +case 165: { + AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; + +case 166: { + AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; + +case 168: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 169: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 170: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 172: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 173: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 175: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 176: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 177: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 179: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 180: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 181: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 182: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 183: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 184: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 186: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 187: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 188: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 189: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 190: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 192: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 193: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 194: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 195: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 197: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 198: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 199: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 200: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 202: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 204: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 206: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 208: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 210: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 212: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 214: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 216: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 218: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 220: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 222: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 224: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 226: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 228: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 229: { + sym(1).ival = QSOperator::Assign; +} break; + +case 230: { + sym(1).ival = QSOperator::InplaceMul; +} break; + +case 231: { + sym(1).ival = QSOperator::InplaceDiv; +} break; + +case 232: { + sym(1).ival = QSOperator::InplaceMod; +} break; + +case 233: { + sym(1).ival = QSOperator::InplaceAdd; +} break; + +case 234: { + sym(1).ival = QSOperator::InplaceSub; +} break; + +case 235: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; + +case 236: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; + +case 237: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; + +case 238: { + sym(1).ival = QSOperator::InplaceAnd; +} break; + +case 239: { + sym(1).ival = QSOperator::InplaceXor; +} break; + +case 240: { + sym(1).ival = QSOperator::InplaceOr; +} break; + +case 242: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 243: { + sym(1).Node = 0; +} break; + +case 246: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 247: { + sym(1).Node = 0; +} break; + +case 264: { + AST::Block *node = new (pool) AST::Block(sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 265: { + sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); +} break; + +case 266: { + sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); +} break; + +case 267: { + sym(1).Node = 0; +} break; + +case 268: { + sym(1).Node = sym(1).StatementList->finish (); +} break; + +case 270: { + AST::VariableStatement *node = new (pool) AST::VariableStatement( + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 271: { + sym(1).ival = T_CONST; +} break; + +case 272: { + sym(1).ival = T_VAR; +} break; + +case 273: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; + +case 274: { + AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 275: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; + +case 276: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; + +case 277: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 278: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 279: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 280: { + sym(1).Node = 0; +} break; + +case 282: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 283: { + sym(1).Node = 0; +} break; + +case 285: { + AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; + +case 287: { + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 288: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(6); + sym(1).Node = node; +} break; + +case 289: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 291: { + AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 292: { + AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 293: { + AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; + +case 294: { + AST::LocalForStatement *node = new (pool) AST::LocalForStatement( + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; + +case 295: { + AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; + +case 296: { + AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; + +case 298: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 300: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 302: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 304: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 306: { + AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 307: { + AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 308: { + AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 309: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 310: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; + +case 311: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); +} break; + +case 312: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); +} break; + +case 313: { + sym(1).Node = 0; +} break; + +case 314: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; + +case 315: { + AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; + +case 316: { + AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 317: +case 318: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 319: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 321: { + AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 322: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 323: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 324: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 325: { + AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 326: { + AST::Finally *node = new (pool) AST::Finally(sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; + +case 328: { + AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 329: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 330: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (! stringRef(2).isNull()) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 331: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 332: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 333: { + sym(1).Node = 0; +} break; + +case 334: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; + +case 335: { + sym(1).Node = 0; +} break; + +case 337: { + sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); +} break; + +case 339: { + sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); +} break; + +case 340: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); +} break; + +case 341: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); +} break; + +case 342: { + sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); +} break; + +case 343: { + sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); +} break; + +case 344: { + stringRef(1) = QStringRef(); +} break; + +case 346: { + sym(1).Node = 0; +} break; + + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.spell = yytokenspell; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = qApp->translate("QQmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].spell = yytokenspell; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->tokenValue(); + token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QQmlParser", "Syntax error"); + else + msg = qApp->translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = qApp->translate("QQmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + diff --git a/src/qml/qml/parser/qqmljsparser_p.h b/src/qml/qml/parser/qqmljsparser_p.h new file mode 100644 index 0000000000..ad532c32c7 --- /dev/null +++ b/src/qml/qml/parser/qqmljsparser_p.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// 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. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QQMLJSPARSER_P_H +#define QQMLJSPARSER_P_H + +#include "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include "qqmljsast_p.h" +#include "qqmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Parser: protected QQmlJSGrammar +{ +public: + union Value { + int ival; + double dval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline QStringRef &stringRef(int index) + { return string_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + MemoryPool *pool; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + QStringRef *string_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + QStringRef spell; + }; + + double yylval; + QStringRef yytokenspell; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QQmlJS + + + +#define J_SCRIPT_REGEXPLITERAL_RULE1 79 + +#define J_SCRIPT_REGEXPLITERAL_RULE2 80 + +QT_QML_END_NAMESPACE + + + +#endif // QQMLJSPARSER_P_H diff --git a/src/qml/qml/qlistmodelinterface.cpp b/src/qml/qml/qlistmodelinterface.cpp new file mode 100644 index 0000000000..3ea0d3f155 --- /dev/null +++ b/src/qml/qml/qlistmodelinterface.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtDeclaractive module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlistmodelinterface_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QListModelInterface + \brief The QListModelInterface class can be subclassed to provide C++ models to QQuickGraphics Views + + This class is comprised primarily of pure virtual functions which + you must implement in a subclass. You can then use the subclass + as a model for a QQuickGraphics view, such as a QQuickListView. +*/ + +/*! \fn QListModelInterface::QListModelInterface(QObject *parent) + Constructs a QListModelInterface with the specified \a parent. +*/ + + /*! \fn QListModelInterface::QListModelInterface(QObjectPrivate &dd, QObject *parent) + + \internal + */ + +/*! \fn QListModelInterface::~QListModelInterface() + The destructor is virtual. + */ + +/*! \fn int QListModelInterface::count() const + Returns the number of data entries in the model. +*/ + +/*! \fn QVariant QListModelInterface::data(int index, int role) const + Returns the data at the given \a index for the specified \a roles. +*/ + +/*! \fn QList<int> QListModelInterface::roles() const + Returns the list of roles for which the list model interface + provides data. +*/ + +/*! \fn QString QListModelInterface::toString(int role) const + Returns a string description of the specified \a role. +*/ + +/*! \fn void QListModelInterface::itemsInserted(int index, int count) + Emit this signal when \a count items are inserted at \a index. + */ + +/*! \fn void QListModelInterface::itemsRemoved(int index, int count) + Emit this signal when \a count items are removed at \a index. + */ + +/*! \fn void QListModelInterface::itemsMoved(int from, int to, int count) + Emit this signal when \a count items are moved from index \a from + to index \a to. + */ + +/*! \fn void QListModelInterface::itemsChanged(int index, int count, const QList<int> &roles) + Emit this signal when \a count items at \a index have had their + \a roles changed. + */ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qlistmodelinterface_p.h b/src/qml/qml/qlistmodelinterface_p.h new file mode 100644 index 0000000000..c644ce88e6 --- /dev/null +++ b/src/qml/qml/qlistmodelinterface_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLISTMODELINTERFACE_H +#define QLISTMODELINTERFACE_H + +#include <QtCore/QHash> +#include <QtCore/QVariant> + +#include <private/qtqmlglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class Q_QML_PRIVATE_EXPORT QListModelInterface : public QObject +{ + Q_OBJECT + public: + QListModelInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~QListModelInterface() {} + + virtual int count() const = 0; + virtual QVariant data(int index, int role) const = 0; + + virtual QList<int> roles() const = 0; + virtual QString toString(int role) const = 0; + + Q_SIGNALS: + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count, const QList<int> &roles); + + protected: + QListModelInterface(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) {} +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QTREEMODELINTERFACE_H diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri new file mode 100644 index 0000000000..0ce7c7ed5c --- /dev/null +++ b/src/qml/qml/qml.pri @@ -0,0 +1,125 @@ +SOURCES += \ + $$PWD/qquickapplication.cpp \ + $$PWD/qqmlinstruction.cpp \ + $$PWD/qquicklistmodel.cpp \ + $$PWD/qquicklistmodelworkeragent.cpp \ + $$PWD/qqmlopenmetaobject.cpp \ + $$PWD/qqmlvmemetaobject.cpp \ + $$PWD/qqmlengine.cpp \ + $$PWD/qqmlexpression.cpp \ + $$PWD/qqmlbinding.cpp \ + $$PWD/qqmlproperty.cpp \ + $$PWD/qqmlcomponent.cpp \ + $$PWD/qqmlincubator.cpp \ + $$PWD/qqmlcontext.cpp \ + $$PWD/qqmlcustomparser.cpp \ + $$PWD/qqmlpropertyvaluesource.cpp \ + $$PWD/qqmlpropertyvalueinterceptor.cpp \ + $$PWD/qqmlproxymetaobject.cpp \ + $$PWD/qqmlvme.cpp \ + $$PWD/qqmlcompiler.cpp \ + $$PWD/qqmlcompileddata.cpp \ + $$PWD/qqmlboundsignal.cpp \ + $$PWD/qqmlmetatype.cpp \ + $$PWD/qqmlstringconverters.cpp \ + $$PWD/qqmlparserstatus.cpp \ + $$PWD/qqmltypeloader.cpp \ + $$PWD/qqmlinfo.cpp \ + $$PWD/qqmlerror.cpp \ + $$PWD/qqmlscript.cpp \ + $$PWD/qqmlrewrite.cpp \ + $$PWD/qqmlvaluetype.cpp \ + $$PWD/qqmlaccessors.cpp \ + $$PWD/qqmlxmlhttprequest.cpp \ + $$PWD/qqmlwatcher.cpp \ + $$PWD/qqmlcleanup.cpp \ + $$PWD/qqmlpropertycache.cpp \ + $$PWD/qqmlnotifier.cpp \ + $$PWD/qqmlintegercache.cpp \ + $$PWD/qqmltypenotavailable.cpp \ + $$PWD/qqmltypenamecache.cpp \ + $$PWD/qqmlscriptstring.cpp \ + $$PWD/qquickworkerscript.cpp \ + $$PWD/qqmlimageprovider.cpp \ + $$PWD/qqmlnetworkaccessmanagerfactory.cpp \ + $$PWD/qqmldirparser.cpp \ + $$PWD/qqmlextensionplugin.cpp \ + $$PWD/qqmlimport.cpp \ + $$PWD/qqmllist.cpp \ + $$PWD/qqmllocale.cpp \ + $$PWD/qlistmodelinterface.cpp + +HEADERS += \ + $$PWD/qqmlglobal_p.h \ + $$PWD/qqmlinstruction_p.h \ + $$PWD/qquicklistmodel_p.h\ + $$PWD/qquicklistmodel_p_p.h\ + $$PWD/qquicklistmodelworkeragent_p.h \ + $$PWD/qqmlopenmetaobject_p.h \ + $$PWD/qqmlvmemetaobject_p.h \ + $$PWD/qqml.h \ + $$PWD/qquickapplication_p.h \ + $$PWD/qqmlbinding_p.h \ + $$PWD/qqmlbinding_p_p.h \ + $$PWD/qqmlproperty.h \ + $$PWD/qqmlcomponent.h \ + $$PWD/qqmlcomponent_p.h \ + $$PWD/qqmlincubator.h \ + $$PWD/qqmlincubator_p.h \ + $$PWD/qqmlcustomparser_p.h \ + $$PWD/qqmlcustomparser_p_p.h \ + $$PWD/qqmlpropertyvaluesource.h \ + $$PWD/qqmlpropertyvalueinterceptor_p.h \ + $$PWD/qqmlboundsignal_p.h \ + $$PWD/qqmlparserstatus.h \ + $$PWD/qqmlproxymetaobject_p.h \ + $$PWD/qqmlvme_p.h \ + $$PWD/qqmlcompiler_p.h \ + $$PWD/qqmlengine_p.h \ + $$PWD/qqmlexpression_p.h \ + $$PWD/qqmlprivate.h \ + $$PWD/qqmlmetatype_p.h \ + $$PWD/qqmlengine.h \ + $$PWD/qqmlcontext.h \ + $$PWD/qqmlexpression.h \ + $$PWD/qqmlstringconverters_p.h \ + $$PWD/qqmlinfo.h \ + $$PWD/qqmlproperty_p.h \ + $$PWD/qqmlcontext_p.h \ + $$PWD/qqmltypeloader_p.h \ + $$PWD/qqmllist.h \ + $$PWD/qqmllist_p.h \ + $$PWD/qqmldata_p.h \ + $$PWD/qqmlerror.h \ + $$PWD/qqmlscript_p.h \ + $$PWD/qqmlrewrite_p.h \ + $$PWD/qqmlvaluetype_p.h \ + $$PWD/qqmlaccessors_p.h \ + $$PWD/qqmlxmlhttprequest_p.h \ + $$PWD/qqmlwatcher_p.h \ + $$PWD/qqmlcleanup_p.h \ + $$PWD/qqmlpropertycache_p.h \ + $$PWD/qqmlnotifier_p.h \ + $$PWD/qqmlintegercache_p.h \ + $$PWD/qqmltypenotavailable_p.h \ + $$PWD/qqmltypenamecache_p.h \ + $$PWD/qqmlscriptstring.h \ + $$PWD/qquickworkerscript_p.h \ + $$PWD/qqmlguard_p.h \ + $$PWD/qqmlimageprovider.h \ + $$PWD/qqmlnetworkaccessmanagerfactory.h \ + $$PWD/qqmldirparser_p.h \ + $$PWD/qqmlextensioninterface.h \ + $$PWD/qqmlimport_p.h \ + $$PWD/qqmlextensionplugin.h \ + $$PWD/qqmlnullablevalue_p_p.h \ + $$PWD/qqmlscriptstring_p.h \ + $$PWD/qqmllocale_p.h \ + $$PWD/qlistmodelinterface_p.h \ + $$PWD/qqmlcomponentattached_p.h + +include(parser/parser.pri) +include(rewriter/rewriter.pri) +include(ftw/ftw.pri) +include(v4/v4.pri) +include(v8/v8.pri) diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h new file mode 100644 index 0000000000..32da2c616e --- /dev/null +++ b/src/qml/qml/qqml.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQML_H +#define QQML_H + +#include <QtQml/qqmlprivate.h> +#include <QtQml/qqmlparserstatus.h> +#include <QtQml/qqmlpropertyvaluesource.h> +#include <QtQml/qqmllist.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qmetaobject.h> + +QT_BEGIN_HEADER + +#define QML_VERSION 0x020000 +#define QML_VERSION_STR "2.0" + +#define QML_DECLARE_TYPE(TYPE) \ + Q_DECLARE_METATYPE(TYPE *) \ + Q_DECLARE_METATYPE(QQmlListProperty<TYPE>) + +#define QML_DECLARE_TYPE_HASMETATYPE(TYPE) \ + Q_DECLARE_METATYPE(QQmlListProperty<TYPE>) + +#define QML_DECLARE_INTERFACE(INTERFACE) \ + QML_DECLARE_TYPE(INTERFACE) + +#define QML_DECLARE_INTERFACE_HASMETATYPE(INTERFACE) \ + QML_DECLARE_TYPE_HASMETATYPE(INTERFACE) + +enum { /* TYPEINFO flags */ + QML_HAS_ATTACHED_PROPERTIES = 0x01 +}; + +#define QML_DECLARE_TYPEINFO(TYPE, FLAGS) \ +QT_BEGIN_NAMESPACE \ +template <> \ +class QQmlTypeInfo<TYPE > \ +{ \ +public: \ + enum { \ + hasAttachedProperties = (((FLAGS) & QML_HAS_ATTACHED_PROPERTIES) == QML_HAS_ATTACHED_PROPERTIES) \ + }; \ +}; \ +QT_END_NAMESPACE + +QT_BEGIN_NAMESPACE + + +class QQmlPropertyValueInterceptor; + +template<typename T> +int qmlRegisterType() +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + 0, 0, + QString(), + + 0, 0, 0, 0, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); + +template<typename T> +int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + 0, 0, + reason, + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +template<typename T> +int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +template<typename T, int metaObjectRevision> +int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 1, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + metaObjectRevision + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +template<typename T, int metaObjectRevision> +int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 1, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, 0, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + metaObjectRevision + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + + +template<typename T, typename E> +int qmlRegisterExtendedType() +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + 0, 0, + QString(), + + 0, 0, 0, 0, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + QQmlPrivate::createParent<E>, &E::staticMetaObject, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +template<typename T, typename E> +int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, + const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlAttachedPropertiesFunc attached = QQmlPrivate::attachedPropertiesFunc<E>(); + const QMetaObject * attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<E>(); + if (!attached) { + attached = QQmlPrivate::attachedPropertiesFunc<T>(); + attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<T>(); + } + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + attached, + attachedMetaObject, + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + QQmlPrivate::createParent<E>, &E::staticMetaObject, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +template<typename T> +int qmlRegisterInterface(const char *typeName) +{ + QByteArray name(typeName); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterInterface qmlInterface = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + + qobject_interface_iid<T *>() + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::InterfaceRegistration, &qmlInterface); +} + +template<typename T> +int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, + const char *qmlName, QQmlCustomParser *parser) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QQmlListProperty<" + name + ">"); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), + qRegisterMetaType<QQmlListProperty<T> >(listName.constData()), + sizeof(T), QQmlPrivate::createInto<T>, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QQmlPrivate::attachedPropertiesFunc<T>(), + QQmlPrivate::attachedPropertiesMetaObject<T>(), + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + parser, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +class QQmlContext; +class QQmlEngine; +class QJSValue; +class QJSEngine; +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); + +template<typename T> +QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) +{ + static int idx = -1; + return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, create); +} + +// For the use of QtQuick1 module +Q_QML_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor); + +inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, + QJSValue (*callback)(QQmlEngine *, QJSEngine *)) +{ + QQmlPrivate::RegisterModuleApi api = { + 0, + + uri, versionMajor, versionMinor, + + callback, 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::ModuleApiRegistration, &api); +} + +inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, + QObject *(*callback)(QQmlEngine *, QJSEngine *)) +{ + QQmlPrivate::RegisterModuleApi api = { + 0, + + uri, versionMajor, versionMinor, + + 0, callback + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::ModuleApiRegistration, &api); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QObject) +Q_DECLARE_METATYPE(QVariant) + +QT_END_HEADER + +#endif // QQML_H diff --git a/src/qml/qml/qqmlaccessors.cpp b/src/qml/qml/qqmlaccessors.cpp new file mode 100644 index 0000000000..ceb4f44789 --- /dev/null +++ b/src/qml/qml/qqmlaccessors.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlaccessors_p.h" + +#include "qqmldata_p.h" +#include "qqmlnotifier_p.h" + +QT_BEGIN_NAMESPACE + +struct AccessorProperties { + AccessorProperties(); + + QReadWriteLock lock; + QHash<const QMetaObject *, QQmlAccessorProperties::Properties> properties; +}; + +Q_GLOBAL_STATIC(AccessorProperties, accessorProperties) + +QML_PRIVATE_ACCESSOR(QObject, QString, objectName, objectName) + +static void QObject_objectNameNotifier(QObject *object, intptr_t, QQmlNotifier **notifier) +{ + *notifier = QQmlData::get(object, true)->objectNameNotifier(); +} + +static QQmlAccessors QObject_objectName = { QObject_objectNameRead, + QObject_objectNameNotifier }; + +QML_DECLARE_PROPERTIES(QObject) { + { QML_PROPERTY_NAME(objectName), 0, &QObject_objectName } +}; + +static void buildNameMask(QQmlAccessorProperties::Properties &properties) +{ + quint32 mask = 0; + + for (int ii = 0; ii < properties.count; ++ii) { + Q_ASSERT(strlen(properties.properties[ii].name) == properties.properties[ii].nameLength); + Q_ASSERT(properties.properties[ii].nameLength > 0); + + mask |= (1 << qMin(31U, properties.properties[ii].nameLength - 1)); + } + + properties.nameMask = mask; +} + +AccessorProperties::AccessorProperties() +{ + // Pre-seed QObject::objectName accessor + typedef QQmlAccessorProperties::Properties P; + properties.insert(&QObject::staticMetaObject, + P(qqml_accessor_properties_QObject, + sizeof(qqml_accessor_properties_QObject) / + sizeof(QQmlAccessorProperties::Property))); +} + +QQmlAccessorProperties::Properties::Properties(Property *properties, int count) +: count(count), properties(properties) +{ + buildNameMask(*this); +} + +QQmlAccessorProperties::Properties +QQmlAccessorProperties::properties(const QMetaObject *mo) +{ + AccessorProperties *This = accessorProperties(); + + QReadLocker lock(&This->lock); + return This->properties.value(mo); +} + +void QQmlAccessorProperties::registerProperties(const QMetaObject *mo, int count, + Property *props) +{ + Q_ASSERT(count > 0); + + Properties properties(props, count); + + AccessorProperties *This = accessorProperties(); + + QWriteLocker lock(&This->lock); + + Q_ASSERT(!This->properties.contains(mo) || This->properties.value(mo) == properties); + + This->properties.insert(mo, properties); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h new file mode 100644 index 0000000000..a603bede9f --- /dev/null +++ b/src/qml/qml/qqmlaccessors_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLACCESSORS_P_H +#define QQMLACCESSORS_P_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qvector.h> +#include <QtCore/qhash.h> +#include <QtCore/QReadWriteLock> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlNotifier; + +// QML "accessor properties" allow V4 and V8 to bypass Qt's meta system to read and, more +// importantly, subscribe to properties directly. Any property that is primarily read +// from bindings is a candidate for inclusion as an accessor property. +// +// To define accessor properties, use the QML_DECLARE_PROPERTIES() and QML_DEFINE_PROPERTIES() +// macros. The QML_DECLARE_PROPERTIES() macro is used to specify the properties, and the +// QML_DEFINE_PROPERTIES() macro to register the properties with the +// QQmlAccessorProperties singleton. +// +// A class with accessor properties must also add the Q_CLASSINFO("qt_HasQmlAccessors", "true") +// tag to its declaration. This is essential for QML to maintain internal consistency, +// and forgetting to do so will probably cause your application to qFatal() with a +// helpful reminder of this requirement. +// +// It is important that QML_DEFINE_PROPERTIES() has been called before QML ever sees +// the type with the accessor properties. As QML_DEFINE_PROPERTIES() is idempotent, it is +// recommended to call it in the type's constructor as well as when the type is registered +// as a QML element (if it ever is). QML_DEFINE_PROPERTIES() is a very cheap operation +// if registration has already occurred. + +#define QML_DECLARE_PROPERTIES(type) \ + static volatile bool qqml_accessor_properties_isregistered_ ## type = false; \ + static QQmlAccessorProperties::Property qqml_accessor_properties_ ## type[] = + +#define QML_DEFINE_PROPERTIES(type) \ + do { \ + if (!qqml_accessor_properties_isregistered_ ## type) { \ + int count = sizeof(qqml_accessor_properties_ ## type) / \ + sizeof(QQmlAccessorProperties::Property); \ + QQmlAccessorProperties::registerProperties(&type::staticMetaObject, count, \ + qqml_accessor_properties_ ## type);\ + qqml_accessor_properties_isregistered_ ## type = true; \ + } \ + } while (false); + +#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable) \ + static void clazz ## _ ## name ## Read(QObject *o, intptr_t, void *rv) \ + { \ + clazz ## Private *d = clazz ## Private::get(static_cast<clazz *>(o)); \ + *static_cast<cpptype *>(rv) = d->variable; \ + } + +#define QML_PROPERTY_NAME(name) #name, sizeof #name - 1 + +class QQmlAccessors +{ +public: + void (*read)(QObject *object, intptr_t property, void *output); + void (*notifier)(QObject *object, intptr_t property, QQmlNotifier **notifier); +}; + +namespace QQmlAccessorProperties { + struct Property { + const char *name; + unsigned int nameLength; + intptr_t data; + QQmlAccessors *accessors; + }; + + struct Properties { + inline Properties(); + Properties(Property *, int); + + bool operator==(const Properties &o) const { + return count == o.count && properties == o.properties; + } + + inline Property *property(const char *name); + + int count; + Property *properties; + quint32 nameMask; + }; + + Properties properties(const QMetaObject *); + void Q_QML_EXPORT registerProperties(const QMetaObject *, int, Property *); +}; + +QQmlAccessorProperties::Property * +QQmlAccessorProperties::Properties::property(const char *name) +{ + if (count == 0) + return 0; + + unsigned int length = strlen(name); + + Q_ASSERT(length); + + if (nameMask & (1 << qMin(31U, length - 1))) { + + for (int ii = 0; ii < count; ++ii) { + if (properties[ii].nameLength == length && 0 == qstrcmp(name, properties[ii].name)) + return &properties[ii]; + } + + } + + return 0; +} + +QQmlAccessorProperties::Properties::Properties() +: count(0), properties(0), nameMask(0) +{ +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLACCESSORS_P_H diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp new file mode 100644 index 0000000000..a19644fb3e --- /dev/null +++ b/src/qml/qml/qqmlbinding.cpp @@ -0,0 +1,551 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlbinding_p.h" +#include "qqmlbinding_p_p.h" + +#include "qqml.h" +#include "qqmlcontext.h" +#include "qqmlinfo.h" +#include "qqmlcompiler_p.h" +#include "qqmldata_p.h" +#include <private/qqmlprofilerservice_p.h> +#include <private/qqmltrace_p.h> + +#include <QVariant> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QQmlAbstractBinding::QQmlAbstractBinding() +: m_prevBinding(0), m_nextBinding(0) +{ +} + +QQmlAbstractBinding::~QQmlAbstractBinding() +{ + Q_ASSERT(m_prevBinding == 0); + Q_ASSERT(*m_mePtr == 0); +} + +/*! +Destroy the binding. Use this instead of calling delete. + +Bindings are free to implement their own memory management, so the delete operator is not +necessarily safe. The default implementation clears the binding, removes it from the object +and calls delete. +*/ +void QQmlAbstractBinding::destroy() +{ + removeFromObject(); + clear(); + + delete this; +} + +/*! +Add this binding to \a object. + +This transfers ownership of the binding to the object, marks the object's property as +being bound. + +However, it does not enable the binding itself or call update() on it. +*/ +void QQmlAbstractBinding::addToObject() +{ + Q_ASSERT(!m_prevBinding); + + QObject *obj = object(); + Q_ASSERT(obj); + + int index = propertyIndex(); + + QQmlData *data = QQmlData::get(obj, true); + + if (index & 0xFF000000) { + // Value type + + int coreIndex = index & 0xFFFFFF; + + // Find the value type proxy (if there is one) + QQmlValueTypeProxyBinding *proxy = 0; + if (data->hasBindingBit(coreIndex)) { + QQmlAbstractBinding *b = data->bindings; + while (b && b->propertyIndex() != coreIndex) + b = b->m_nextBinding; + Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy); + proxy = static_cast<QQmlValueTypeProxyBinding *>(b); + } + + if (!proxy) { + proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); + + Q_ASSERT(proxy->propertyIndex() == coreIndex); + Q_ASSERT(proxy->object() == obj); + + proxy->addToObject(); + } + + m_nextBinding = proxy->m_bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &proxy->m_bindings; + proxy->m_bindings = this; + + } else { + m_nextBinding = data->bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &data->bindings; + data->bindings = this; + + data->setBindingBit(obj, index); + } +} + +/*! +Remove the binding from the object. +*/ +void QQmlAbstractBinding::removeFromObject() +{ + if (m_prevBinding) { + int index = propertyIndex(); + + *m_prevBinding = m_nextBinding; + if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; + m_prevBinding = 0; + m_nextBinding = 0; + + if (index & 0xFF000000) { + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + } else if (QObject *obj = object()) { + QQmlData *data = QQmlData::get(obj, false); + if (data) data->clearBindingBit(index); + } + } +} + +static void bindingDummyDeleter(QQmlAbstractBinding *) +{ +} + +QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer() +{ + if (m_mePtr.value().isNull()) + m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter); + + return m_mePtr.value().toWeakRef(); +} + +void QQmlAbstractBinding::clear() +{ + if (!m_mePtr.isNull()) { + **m_mePtr = 0; + m_mePtr = 0; + } +} + +void QQmlAbstractBinding::retargetBinding(QObject *, int) +{ + qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding."); +} + +QString QQmlAbstractBinding::expression() const +{ + return QLatin1String("<Unknown>"); +} + +void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags) +{ + if (enabled) update(flags); +} + +QQmlBinding::Identifier QQmlBinding::Invalid = -1; + +void QQmlBindingPrivate::refresh() +{ + Q_Q(QQmlBinding); + q->update(); +} + +QQmlBindingPrivate::QQmlBindingPrivate() +: updating(false), enabled(false), target(), targetProperty(0) +{ +} + +QQmlBindingPrivate::~QQmlBindingPrivate() +{ +} + +QQmlBinding * +QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt, + const QString &url, int lineNumber, QObject *parent) +{ + if (id < 0) + return 0; + + QQmlContextData *ctxtdata = QQmlContextData::get(ctxt); + + QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine()); + QQmlCompiledData *cdata = 0; + QQmlTypeData *typeData = 0; + if (engine && ctxtdata && !ctxtdata->url.isEmpty()) { + typeData = engine->typeLoader.get(ctxtdata->url); + cdata = typeData->compiledData(); + } + QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0, parent) : 0; + if (cdata) + cdata->release(); + if (typeData) + typeData->release(); + return rv; +} + +QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt, + QObject *parent) +: QQmlExpression(QQmlContextData::get(ctxt), obj, str, *new QQmlBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, + QObject *parent) +: QQmlExpression(ctxt, obj, str, *new QQmlBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, + QQmlContextData *ctxt, + const QString &url, int lineNumber, int columnNumber, + QObject *parent) +: QQmlExpression(ctxt, obj, str, isRewritten, url, lineNumber, columnNumber, *new QQmlBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +/*! + \internal + + To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>. + For example: + v8::Handle<v8::Function> function; + new QQmlBinding(&function, scope, ctxt); + */ +QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt, + QObject *parent) +: QQmlExpression(ctxt, obj, functionPtr, *new QQmlBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QQmlBinding::~QQmlBinding() +{ +} + +void QQmlBinding::setTarget(const QQmlProperty &prop) +{ + Q_D(QQmlBinding); + d->property = prop; + d->target = d->property.object(); + d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex(); + + update(); +} + +void QQmlBinding::setTarget(QObject *object, + const QQmlPropertyData &core, + QQmlContextData *ctxt) +{ + Q_D(QQmlBinding); + d->property = QQmlPropertyPrivate::restore(object, core, ctxt); + d->target = d->property.object(); + d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex(); + + update(); +} + +QQmlProperty QQmlBinding::property() const +{ + Q_D(const QQmlBinding); + return d->property; +} + +void QQmlBinding::setEvaluateFlags(EvaluateFlags flags) +{ + Q_D(QQmlBinding); + d->setRequiresThisObject(flags & RequiresThisObject); +} + +QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const +{ + Q_D(const QQmlBinding); + return d->requiresThisObject()?RequiresThisObject:None; +} + +void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) +{ + Q_D(QQmlBinding); + + if (!d->enabled || !d->context() || !d->context()->isValid()) + return; + + QQmlTrace trace("General Binding Update"); + trace.addDetail("URL", d->url); + trace.addDetail("Line", d->line); + trace.addDetail("Column", d->columnNumber); + + if (!d->updating) { + QQmlBindingProfiler prof(d->url, d->line, d->column); + prof.addDetail(expression()); + d->updating = true; + + QQmlAbstractExpression::DeleteWatcher watcher(d); + + if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) { + + int idx = d->property.index(); + Q_ASSERT(idx != -1); + + QQmlBinding *t = this; + int status = -1; + void *a[] = { &t, 0, &status, &flags }; + QMetaObject::metacall(d->property.object(), + QMetaObject::WriteProperty, + idx, a); + + } else { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine); + ep->referenceScarceResources(); + + bool isUndefined = false; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local<v8::Value> result = d->v8value(0, &isUndefined); + + trace.event("writing binding result"); + + bool needsErrorData = false; + if (!watcher.wasDeleted() && !d->hasError()) + needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(), + d, result, + isUndefined, flags); + + if (!watcher.wasDeleted()) { + + if (needsErrorData) { + QUrl url = QUrl(d->url); + int line = d->line; + if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>")); + + d->delayedError()->error.setUrl(url); + d->delayedError()->error.setLine(line); + d->delayedError()->error.setColumn(-1); + } + + if (d->hasError()) { + if (!d->delayedError()->addError(ep)) ep->warning(this->error()); + } else { + d->clearError(); + } + + } + + ep->dereferenceScarceResources(); + } + + if (!watcher.wasDeleted()) + d->updating = false; + } else { + QQmlBindingPrivate::printBindingLoopError(d->property); + } +} + +void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop) +{ + qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name()); +} + +void QQmlBindingPrivate::expressionChanged() +{ + Q_Q(QQmlBinding); + q->update(); +} + +void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + Q_D(QQmlBinding); + d->enabled = e; + setNotifyOnValueChanged(e); + + if (e) + update(flags); +} + +bool QQmlBinding::enabled() const +{ + Q_D(const QQmlBinding); + + return d->enabled; +} + +QString QQmlBinding::expression() const +{ + return QQmlExpression::expression(); +} + +int QQmlBinding::propertyIndex() const +{ + Q_D(const QQmlBinding); + return d->targetProperty; +} + +QObject *QQmlBinding::object() const +{ + Q_D(const QQmlBinding); + return d->target; +} + +void QQmlBinding::retargetBinding(QObject *t, int i) +{ + Q_D(QQmlBinding); + d->target = t; + d->targetProperty = i; +} + +QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) +: m_object(o), m_index(index), m_bindings(0) +{ +} + +QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() +{ + while (m_bindings) { + QQmlAbstractBinding *binding = m_bindings; + binding->setEnabled(false, 0); + binding->destroy(); + } +} + +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + if (e) { + QQmlAbstractBinding *bindings = m_bindings; + recursiveEnable(bindings, flags); + } else { + QQmlAbstractBinding *bindings = m_bindings; + recursiveDisable(bindings); + } +} + +void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) +{ + if (!b) + return; + + recursiveEnable(b->m_nextBinding, flags); + + if (b) + b->setEnabled(true, flags); +} + +void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) +{ + if (!b) + return; + + recursiveDisable(b->m_nextBinding); + + if (b) + b->setEnabled(false, 0); +} + +void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags) +{ +} + +QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) +{ + QQmlAbstractBinding *binding = m_bindings; + + while (binding && binding->propertyIndex() != propertyIndex) + binding = binding->m_nextBinding; + + return binding; +} + +/*! +Removes a collection of bindings, corresponding to the set bits in \a mask. +*/ +void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) +{ + QQmlAbstractBinding *binding = m_bindings; + while (binding) { + if (mask & (1 << (binding->propertyIndex() >> 24))) { + QQmlAbstractBinding *remove = binding; + binding = remove->m_nextBinding; + *remove->m_prevBinding = remove->m_nextBinding; + if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding; + remove->m_prevBinding = 0; + remove->m_nextBinding = 0; + remove->destroy(); + } else { + binding = binding->m_nextBinding; + } + } +} + +int QQmlValueTypeProxyBinding::propertyIndex() const +{ + return m_index; +} + +QObject *QQmlValueTypeProxyBinding::object() const +{ + return m_object; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h new file mode 100644 index 0000000000..33823d7e7e --- /dev/null +++ b/src/qml/qml/qqmlbinding_p.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBINDING_P_H +#define QQMLBINDING_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 "qqml.h" +#include "qqmlpropertyvaluesource.h" +#include "qqmlexpression.h" +#include "qqmlproperty.h" +#include "qqmlproperty_p.h" + +#include <QtCore/QObject> +#include <QtCore/QMetaProperty> + +#include <private/qpointervaluepair_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding +{ +public: + typedef QWeakPointer<QQmlAbstractBinding> Pointer; + + QQmlAbstractBinding(); + + virtual void destroy(); + + virtual QString expression() const; + + enum Type { PropertyBinding, ValueTypeProxy }; + virtual Type bindingType() const { return PropertyBinding; } + + // Should return the encoded property index for the binding. Should return this value + // even if the binding is not enabled or added to an object. + // Encoding is: coreIndex | (valueTypeIndex << 24) + virtual int propertyIndex() const = 0; + // Should return the object for the binding. Should return this object even if the + // binding is not enabled or added to the object. + virtual QObject *object() const = 0; + + void setEnabled(bool e) { setEnabled(e, QQmlPropertyPrivate::DontRemoveBinding); } + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags) = 0; + + void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } + virtual void update(QQmlPropertyPrivate::WriteFlags) = 0; + + void addToObject(); + void removeFromObject(); + + static inline Pointer getPointer(QQmlAbstractBinding *p); + +protected: + virtual ~QQmlAbstractBinding(); + void clear(); + + // Called by QQmlPropertyPrivate to "move" a binding to a different property. + // This is only used for alias properties, and only used by QQmlBinding not + // V8 or V4 bindings. The default implementation qFatal()'s to ensure that the + // method is never called for V4 or V8 bindings. + virtual void retargetBinding(QObject *, int); +private: + Pointer weakPointer(); + + friend class QQmlData; + friend class QQmlComponentPrivate; + friend class QQmlValueTypeProxyBinding; + friend class QQmlPropertyPrivate; + friend class QQmlVME; + friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; + + typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; + // To save memory, we also store the rarely used weakPointer() instance in here + QPointerValuePair<QQmlAbstractBinding*, SharedPointer> m_mePtr; + + QQmlAbstractBinding **m_prevBinding; + QQmlAbstractBinding *m_nextBinding; +}; + +class QQmlValueTypeProxyBinding : public QQmlAbstractBinding +{ +public: + QQmlValueTypeProxyBinding(QObject *o, int coreIndex); + + virtual Type bindingType() const { return ValueTypeProxy; } + + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual void update(QQmlPropertyPrivate::WriteFlags); + virtual int propertyIndex() const; + virtual QObject *object() const; + + QQmlAbstractBinding *binding(int propertyIndex); + + void removeBindings(quint32 mask); + +protected: + ~QQmlValueTypeProxyBinding(); + +private: + void recursiveEnable(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); + void recursiveDisable(QQmlAbstractBinding *); + + friend class QQmlAbstractBinding; + QObject *m_object; + int m_index; + QQmlAbstractBinding *m_bindings; +}; + +class QQmlContext; +class QQmlBindingPrivate; +class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlExpression, + public QQmlAbstractBinding +{ +Q_OBJECT +public: + enum EvaluateFlag { None = 0x00, RequiresThisObject = 0x01 }; + Q_DECLARE_FLAGS(EvaluateFlags, EvaluateFlag) + + QQmlBinding(const QString &, QObject *, QQmlContext *, QObject *parent=0); + QQmlBinding(const QString &, QObject *, QQmlContextData *, QObject *parent=0); + QQmlBinding(const QString &, bool isRewritten, QObject *, QQmlContextData *, + const QString &url, int lineNumber, int columnNumber = 0, QObject *parent=0); + QQmlBinding(void *, QObject *, QQmlContextData *, QObject *parent=0); + + void setTarget(const QQmlProperty &); + void setTarget(QObject *, const QQmlPropertyData &, QQmlContextData *); + QQmlProperty property() const; + + void setEvaluateFlags(EvaluateFlags flags); + EvaluateFlags evaluateFlags() const; + + bool enabled() const; + + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); + virtual void update(QQmlPropertyPrivate::WriteFlags flags); + virtual QString expression() const; + virtual int propertyIndex() const; + virtual QObject *object() const; + virtual void retargetBinding(QObject *, int); + + typedef int Identifier; + static Identifier Invalid; + static QQmlBinding *createBinding(Identifier, QObject *, QQmlContext *, + const QString &, int, QObject *parent=0); + + +public Q_SLOTS: + void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } + +protected: + ~QQmlBinding(); + +private: + Q_DECLARE_PRIVATE(QQmlBinding) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlBinding::EvaluateFlags) + +QQmlAbstractBinding::Pointer +QQmlAbstractBinding::getPointer(QQmlAbstractBinding *p) +{ + return p ? p->weakPointer() : Pointer(); +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlBinding*) + +#endif // QQMLBINDING_P_H diff --git a/src/qml/qml/qqmlbinding_p_p.h b/src/qml/qml/qqmlbinding_p_p.h new file mode 100644 index 0000000000..b53c903916 --- /dev/null +++ b/src/qml/qml/qqmlbinding_p_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBINDING_P_P_H +#define QQMLBINDING_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 "qqmlbinding_p.h" + +#include "qqmlproperty.h" +#include "qqmlexpression_p.h" + +QT_BEGIN_NAMESPACE + +class QQmlBindingPrivate : public QQmlExpressionPrivate +{ + Q_DECLARE_PUBLIC(QQmlBinding) +public: + QQmlBindingPrivate(); + ~QQmlBindingPrivate(); + + virtual void expressionChanged(); + + static void printBindingLoopError(QQmlProperty &prop); + +protected: + virtual void refresh(); + +private: + bool updating:1; + bool enabled:1; + int columnNumber; + QQmlProperty property; + + QObject *target; + int targetProperty; +}; + +QT_END_NAMESPACE + +#endif // QQMLBINDING_P_P_H diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp new file mode 100644 index 0000000000..6f552450ef --- /dev/null +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlboundsignal_p.h" + +#include <private/qmetaobjectbuilder_p.h> +#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" +#include <private/qqmlprofilerservice_p.h> +#include <private/qv8debugservice_p.h> + +#include <QtCore/qstringbuilder.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +class QQmlBoundSignalParameters : public QObject +{ +Q_OBJECT +public: + QQmlBoundSignalParameters(const QMetaMethod &, QObject * = 0); + ~QQmlBoundSignalParameters(); + + void setValues(void **); + void clearValues(); + +private: + friend class MetaObject; + int metaCall(QMetaObject::Call, int _id, void **); + struct MetaObject : public QAbstractDynamicMetaObject { + MetaObject(QQmlBoundSignalParameters *b) + : parent(b) {} + + int metaCall(QMetaObject::Call c, int id, void **a) { + return parent->metaCall(c, id, a); + } + QQmlBoundSignalParameters *parent; + }; + + int *types; + void **values; + QMetaObject *myMetaObject; +}; + +static int evaluateIdx = -1; + +QQmlAbstractBoundSignal::QQmlAbstractBoundSignal(QObject *parent) +: QObject(parent) +{ +} + +QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal() +{ +} + +QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0) +{ + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); + + QQml_setParent_noEvent(this, parent); + QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx); +} + +QQmlBoundSignal::QQmlBoundSignal(QQmlContext *ctxt, const QString &val, + QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0) +{ + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); + + QQml_setParent_noEvent(this, parent); + QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx); + + m_expression = new QQmlExpression(ctxt, scope, val); +} + +QQmlBoundSignal::~QQmlBoundSignal() +{ + delete m_expression; + m_expression = 0; +} + +int QQmlBoundSignal::index() const +{ + return m_signal.methodIndex(); +} + +/*! + Returns the signal expression. +*/ +QQmlExpression *QQmlBoundSignal::expression() const +{ + return m_expression; +} + +/*! + Sets the signal expression to \a e. Returns the current signal expression, + or null if there is no signal expression. + + The QQmlBoundSignal instance takes ownership of \a e. The caller is + assumes ownership of the returned QQmlExpression. +*/ +QQmlExpression *QQmlBoundSignal::setExpression(QQmlExpression *e) +{ + QQmlExpression *rv = m_expression; + m_expression = e; + if (m_expression) m_expression->setNotifyOnValueChanged(false); + return rv; +} + +QQmlBoundSignal *QQmlBoundSignal::cast(QObject *o) +{ + QQmlAbstractBoundSignal *s = qobject_cast<QQmlAbstractBoundSignal*>(o); + return static_cast<QQmlBoundSignal *>(s); +} + +int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { + if (!m_expression) + return -1; + if (QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService::startRange(QQmlProfilerService::HandlingSignal); + QQmlProfilerService::rangeData(QQmlProfilerService::HandlingSignal, QLatin1String(m_signal.signature()) % QLatin1String(": ") % m_expression->expression()); + QQmlProfilerService::rangeLocation(QQmlProfilerService::HandlingSignal, m_expression->sourceFile(), m_expression->lineNumber(), m_expression->columnNumber()); + QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.signature())); + } + m_isEvaluating = true; + if (!m_paramsValid) { + if (!m_signal.parameterTypes().isEmpty()) + m_params = new QQmlBoundSignalParameters(m_signal, this); + m_paramsValid = true; + } + + if (m_params) m_params->setValues(a); + if (m_expression && m_expression->engine()) { + QQmlExpressionPrivate::get(m_expression)->value(m_params); + if (m_expression && m_expression->hasError()) + QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error()); + } + if (m_params) m_params->clearValues(); + m_isEvaluating = false; + QQmlProfilerService::endRange(QQmlProfilerService::HandlingSignal); + return -1; + } else { + return QObject::qt_metacall(c, id, a); + } +} + +QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, + QObject *parent) +: QObject(parent), types(0), values(0) +{ + MetaObject *mo = new MetaObject(this); + + // ### Optimize! + QMetaObjectBuilder mob; + mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject); + mob.setClassName("QQmlBoundSignalParameters"); + + QList<QByteArray> paramTypes = method.parameterTypes(); + QList<QByteArray> paramNames = method.parameterNames(); + types = new int[paramTypes.count()]; + for (int ii = 0; ii < paramTypes.count(); ++ii) { + const QByteArray &type = paramTypes.at(ii); + const QByteArray &name = paramNames.at(ii); + + if (name.isEmpty() || type.isEmpty()) { + types[ii] = 0; + continue; + } + + QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData()); + if (QQmlMetaType::isQObject(t)) { + types[ii] = QMetaType::QObjectStar; + QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*"); + prop.setWritable(false); + } else { + QByteArray propType = type; + if (t >= QVariant::UserType || t == QVariant::Invalid) { + QByteArray scope; + QByteArray name; + int scopeIdx = propType.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = propType.left(scopeIdx); + name = propType.mid(scopeIdx + 2); + } else { + name = propType; + } + const QMetaObject *meta; + if (scope == "Qt") + meta = &QObject::staticQtMetaObject; + else + meta = parent->parent()->metaObject(); //### assumes parent->parent() + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) { + t = QVariant::Int; + propType = "int"; + break; + } + } + } + types[ii] = t; + QMetaPropertyBuilder prop = mob.addProperty(name, propType); + prop.setWritable(false); + } + } + myMetaObject = mob.toMetaObject(); + *static_cast<QMetaObject *>(mo) = *myMetaObject; + + d_ptr->metaObject = mo; +} + +QQmlBoundSignalParameters::~QQmlBoundSignalParameters() +{ + delete [] types; + free(myMetaObject); +} + +void QQmlBoundSignalParameters::setValues(void **v) +{ + values = v; +} + +void QQmlBoundSignalParameters::clearValues() +{ + values = 0; +} + +int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (!values) + return -1; + + if (c == QMetaObject::ReadProperty && id >= 1) { + int t = types[id - 1]; + void *p = a[0]; + QMetaType::destruct(t, p); + QMetaType::construct(t, p, values[id]); + return -1; + } else { + return qt_metacall(c, id, a); + } +} + +QT_END_NAMESPACE + +#include <qqmlboundsignal.moc> diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h new file mode 100644 index 0000000000..11386159cb --- /dev/null +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBOUNDSIGNAL_P_H +#define QQMLBOUNDSIGNAL_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 "qqmlexpression.h" + +#include <QtCore/qmetaobject.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_EXPORT QQmlAbstractBoundSignal : public QObject +{ + Q_OBJECT +public: + QQmlAbstractBoundSignal(QObject *parent = 0); + virtual ~QQmlAbstractBoundSignal() = 0; +}; + +class QQmlBoundSignalParameters; +class Q_QML_EXPORT QQmlBoundSignal : public QQmlAbstractBoundSignal +{ +public: + QQmlBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *parent); + QQmlBoundSignal(QQmlContext *ctxt, const QString &val, QObject *scope, + const QMetaMethod &signal, QObject *parent); + virtual ~QQmlBoundSignal(); + + int index() const; + + QQmlExpression *expression() const; + QQmlExpression *setExpression(QQmlExpression *); + + bool isEvaluating() const { return m_isEvaluating; } + + static QQmlBoundSignal *cast(QObject *); + +protected: + virtual int qt_metacall(QMetaObject::Call c, int id, void **a); + +private: + QQmlExpression *m_expression; + QMetaMethod m_signal; + bool m_paramsValid : 1; + bool m_isEvaluating : 1; + QQmlBoundSignalParameters *m_params; +}; + +QT_END_NAMESPACE + +#endif // QQMLBOUNDSIGNAL_P_H diff --git a/src/qml/qml/qqmlcleanup.cpp b/src/qml/qml/qqmlcleanup.cpp new file mode 100644 index 0000000000..21ea02ee17 --- /dev/null +++ b/src/qml/qml/qqmlcleanup.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcleanup_p.h" + +#include "qqmlengine_p.h" + +QT_BEGIN_NAMESPACE + +/*! +\internal +\class QQmlCleanup +\brief The QQmlCleanup provides a callback when a QQmlEngine is deleted. + +Any object that needs cleanup to occur before the QQmlEngine's V8 engine is +destroyed should inherit from QQmlCleanup. The clear() virtual method will be +called by QQmlEngine just before it destroys the context. +*/ + + +/* +Create a QQmlCleanup that is not associated with any engine. +*/ +QQmlCleanup::QQmlCleanup() +: prev(0), next(0), engine(0) +{ +} + +/*! +Create a QQmlCleanup for \a engine +*/ +QQmlCleanup::QQmlCleanup(QQmlEngine *engine) +: prev(0), next(0), engine(0) +{ + if (!engine) + return; + + addToEngine(engine); +} + +/*! +Adds this object to \a engine's cleanup list. hasEngine() must be false +before calling this method. +*/ +void QQmlCleanup::addToEngine(QQmlEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(QQmlEnginePrivate::isEngineThread(engine)); + + this->engine = engine; + + QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine); + + if (p->cleanup) next = p->cleanup; + p->cleanup = this; + prev = &p->cleanup; + if (next) next->prev = &next; +} + +/*! +\fn bool QQmlCleanup::hasEngine() const + +Returns true if this QQmlCleanup is associated with an engine, otherwise false. +*/ + +/*! +\internal +*/ +QQmlCleanup::~QQmlCleanup() +{ + Q_ASSERT(!prev || engine); + Q_ASSERT(!prev || QQmlEnginePrivate::isEngineThread(engine)); + + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcleanup_p.h b/src/qml/qml/qqmlcleanup_p.h new file mode 100644 index 0000000000..2b7747d630 --- /dev/null +++ b/src/qml/qml/qqmlcleanup_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCLEANUP_P_H +#define QQMLCLEANUP_P_H + +#include <QtQml/qtqmlglobal.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. +// + +QT_BEGIN_NAMESPACE + +class QQmlEngine; + +// Exported for QtQuick1 +class Q_QML_EXPORT QQmlCleanup +{ +public: + QQmlCleanup(); + QQmlCleanup(QQmlEngine *); + virtual ~QQmlCleanup(); + + bool hasEngine() const { return prev != 0; } + void addToEngine(QQmlEngine *); +protected: + virtual void clear() = 0; + +private: + friend class QQmlEnginePrivate; + QQmlCleanup **prev; + QQmlCleanup *next; + + // Only used for asserts + QQmlEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QQMLCLEANUP_P_H + diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp new file mode 100644 index 0000000000..6a68784219 --- /dev/null +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcompiler_p.h" +#include "qqmlengine.h" +#include "qqmlcomponent.h" +#include "qqmlcomponent_p.h" +#include "qqmlcontext.h" +#include "qqmlcontext_p.h" +#ifdef QML_THREADED_VME_INTERPRETER +#include "qqmlvme_p.h" +#endif + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +int QQmlCompiledData::pack(const char *data, size_t size) +{ + const char *p = packData.constData(); + unsigned int ps = packData.size(); + + for (unsigned int ii = 0; (ii + size) <= ps; ii += sizeof(int)) { + if (0 == ::memcmp(p + ii, data, size)) + return ii; + } + + int rv = packData.size(); + packData.append(data, size); + return rv; +} + +int QQmlCompiledData::indexForString(const QString &data) +{ + int idx = primitives.indexOf(data); + if (idx == -1) { + idx = primitives.count(); + primitives << data; + } + return idx; +} + +int QQmlCompiledData::indexForByteArray(const QByteArray &data) +{ + int idx = datas.indexOf(data); + if (idx == -1) { + idx = datas.count(); + datas << data; + } + return idx; +} + +int QQmlCompiledData::indexForUrl(const QUrl &data) +{ + int idx = urls.indexOf(data); + if (idx == -1) { + idx = urls.count(); + urls << data; + } + return idx; +} + +QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) +: engine(engine), importCache(0), root(0), rootPropertyCache(0) +{ + Q_ASSERT(engine); + + bytecode.reserve(1024); +} + +void QQmlCompiledData::destroy() +{ + if (engine && hasEngine()) + QQmlEnginePrivate::deleteInEngineThread(engine, this); + else + delete this; +} + +QQmlCompiledData::~QQmlCompiledData() +{ + clear(); + + for (int ii = 0; ii < types.count(); ++ii) { + if (types.at(ii).component) + types.at(ii).component->release(); + if (types.at(ii).typePropertyCache) + types.at(ii).typePropertyCache->release(); + } + + for (int ii = 0; ii < propertyCaches.count(); ++ii) + propertyCaches.at(ii)->release(); + + for (int ii = 0; ii < contextCaches.count(); ++ii) + contextCaches.at(ii)->release(); + + for (int ii = 0; ii < scripts.count(); ++ii) + scripts.at(ii)->release(); + + if (importCache) + importCache->release(); + + if (rootPropertyCache) + rootPropertyCache->release(); +} + +void QQmlCompiledData::clear() +{ + for (int ii = 0; ii < programs.count(); ++ii) + qPersistentDispose(programs[ii].bindings); +} + +const QMetaObject *QQmlCompiledData::TypeReference::metaObject() const +{ + if (type) { + return type->metaObject(); + } else { + Q_ASSERT(component); + return component->root; + } +} + +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QQmlPropertyCache *QQmlCompiledData::TypeReference::propertyCache() const +{ + if (type) + return typePropertyCache; + else + return component->rootPropertyCache; +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngine *engine) +{ + if (typePropertyCache) { + return typePropertyCache; + } else if (type) { + typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); + typePropertyCache->addref(); + return typePropertyCache; + } else { + return component->rootPropertyCache; + } +} + + +void QQmlCompiledData::dumpInstructions() +{ + if (!name.isEmpty()) + qWarning() << name; + qWarning().nospace() << "Index\tOperation\t\tData1\tData2\tData3\tComments"; + qWarning().nospace() << "-------------------------------------------------------------------------------"; + + const char *instructionStream = bytecode.constData(); + const char *endInstructionStream = bytecode.constData() + bytecode.size(); + + int instructionCount = 0; + while (instructionStream < endInstructionStream) { + QQmlInstruction *instr = (QQmlInstruction *)instructionStream; + dump(instr, instructionCount); + instructionStream += QQmlInstruction::size(instructionType(instr)); + instructionCount++; + } + + qWarning().nospace() << "-------------------------------------------------------------------------------"; +} + +int QQmlCompiledData::addInstructionHelper(QQmlInstruction::Type type, QQmlInstruction &instr) +{ +#ifdef QML_THREADED_VME_INTERPRETER + instr.common.code = QQmlVME::instructionJumpTable()[static_cast<int>(type)]; +#else + instr.common.instructionType = type; +#endif + int ptrOffset = bytecode.size(); + int size = QQmlInstruction::size(type); + if (bytecode.capacity() <= bytecode.size() + size) + bytecode.reserve(bytecode.size() + size + 512); + bytecode.append(reinterpret_cast<const char *>(&instr), size); + return ptrOffset; +} + +int QQmlCompiledData::nextInstructionIndex() +{ + return bytecode.size(); +} + +QQmlInstruction *QQmlCompiledData::instruction(int index) +{ + return (QQmlInstruction *)(bytecode.constData() + index); +} + +QQmlInstruction::Type QQmlCompiledData::instructionType(const QQmlInstruction *instr) +{ +#ifdef QML_THREADED_VME_INTERPRETER + void **jumpTable = QQmlVME::instructionJumpTable(); + void *code = instr->common.code; + +# define QML_CHECK_INSTR_CODE(I, FMT) \ + if (jumpTable[static_cast<int>(QQmlInstruction::I)] == code) \ + return QQmlInstruction::I; + + FOR_EACH_QML_INSTR(QML_CHECK_INSTR_CODE) + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid instruction address"); + return static_cast<QQmlInstruction::Type>(0); +# undef QML_CHECK_INSTR_CODE +#else + return static_cast<QQmlInstruction::Type>(instr->common.instructionType); +#endif +} + +void QQmlCompiledData::initialize(QQmlEngine *engine) +{ + Q_ASSERT(!hasEngine()); + QQmlCleanup::addToEngine(engine); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp new file mode 100644 index 0000000000..65247e1e80 --- /dev/null +++ b/src/qml/qml/qqmlcompiler.cpp @@ -0,0 +1,3882 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcompiler_p.h" + +#include "qqmlpropertyvaluesource.h" +#include "qqmlcomponent.h" +#include <private/qmetaobjectbuilder_p.h> +#include <private/qfastmetabuilder_p.h> +#include "qqmlstringconverters_p.h" +#include "qqmlengine_p.h" +#include "qqmlengine.h" +#include "qqmlcontext.h" +#include "qqmlmetatype_p.h" +#include "qqmlcustomparser_p_p.h" +#include "qqmlcontext_p.h" +#include "qqmlcomponent_p.h" +#include <private/qqmljsast_p.h> +#include "qqmlvmemetaobject_p.h" +#include "qqmlexpression_p.h" +#include "qqmlproperty_p.h" +#include "qqmlrewrite_p.h" +#include "qqmlscriptstring.h" +#include "qqmlglobal_p.h" +#include "qqmlbinding_p.h" +#include <private/qv4compiler_p.h> + +#include <QColor> +#include <QDebug> +#include <QPointF> +#include <QSizeF> +#include <QRectF> +#include <QAtomicInt> +#include <QtCore/qdebug.h> +#include <QtCore/qdatetime.h> + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<qreal>) +Q_DECLARE_METATYPE(QList<bool>) +Q_DECLARE_METATYPE(QList<QString>) +Q_DECLARE_METATYPE(QList<QUrl>) + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); +DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS); + +using namespace QQmlJS; +using namespace QQmlScript; +using namespace QQmlCompilerTypes; + +static QString id_string(QLatin1String("id")); +static QString on_string(QLatin1String("on")); +static QString Changed_string(QLatin1String("Changed")); +static QString Component_string(QLatin1String("Component")); +static QString Component_import_string(QLatin1String("QML/Component")); +static QString qsTr_string(QLatin1String("qsTr")); +static QString qsTrId_string(QLatin1String("qsTrId")); + +/*! + Instantiate a new QQmlCompiler. +*/ +QQmlCompiler::QQmlCompiler(QQmlPool *pool) +: pool(pool), output(0), engine(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1), + cachedTranslationContextIndex(-1), componentStats(0) +{ + if (compilerStatDump()) + componentStats = pool->New<ComponentStats>(); +} + +/*! + Returns true if the last call to compile() caused errors. + + \sa errors() +*/ +bool QQmlCompiler::isError() const +{ + return !exceptions.isEmpty(); +} + +/*! + Return the list of errors from the last call to compile(), or an empty list + if there were no errors. +*/ +QList<QQmlError> QQmlCompiler::errors() const +{ + return exceptions; +} + +/*! + Returns true if \a name refers to an attached property, false otherwise. + + Attached property names are those that start with a capital letter. +*/ +bool QQmlCompiler::isAttachedPropertyName(const QString &name) +{ + return isAttachedPropertyName(QHashedStringRef(&name)); +} + +bool QQmlCompiler::isAttachedPropertyName(const QHashedStringRef &name) +{ + return !name.isEmpty() && name.at(0).isUpper(); +} + +/*! + Returns true if \a name refers to a signal property, false otherwise. + + Signal property names are those that start with "on", followed by a first + character which is either a capital letter or one or more underscores followed + by a capital letter, which is then followed by other allowed characters. + + Note that although ECMA-262r3 supports dollarsigns and escaped unicode + character codes in property names, for simplicity and performance reasons + QML only supports letters, numbers and underscores. +*/ +bool QQmlCompiler::isSignalPropertyName(const QString &name) +{ + return isSignalPropertyName(QStringRef(&name)); +} + +bool QQmlCompiler::isSignalPropertyName(const QHashedStringRef &name) +{ + if (name.length() < 3) return false; + if (!name.startsWith(on_string)) return false; + int ns = name.length(); + for (int i = 2; i < ns; ++i) { + const QChar curr = name.at(i); + if (curr.unicode() == '_') continue; + if (curr.isUpper()) return true; + return false; + } + return false; // consists solely of underscores - invalid. +} + +/*! + \macro COMPILE_EXCEPTION + \internal + Inserts an error into the QQmlCompiler error list, and returns false + (failure). + + \a token is used to source the error line and column, and \a desc is the + error itself. \a desc can be an expression that can be piped into QDebug. + + For example: + + \code + COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(property->name)); + \endcode +*/ +#define COMPILE_EXCEPTION_LOCATION(line, column, desc) \ + { \ + QQmlError error; \ + error.setUrl(output->url); \ + error.setLine(line); \ + error.setColumn(column); \ + error.setDescription(desc.trimmed()); \ + exceptions << error; \ + return false; \ + } + +#define COMPILE_EXCEPTION(token, desc) \ + COMPILE_EXCEPTION_LOCATION((token)->location.start.line, (token)->location.start.column, desc) + +/*! + \macro COMPILE_CHECK + \internal + Returns false if \a is false, otherwise does nothing. +*/ +#define COMPILE_CHECK(a) \ + { \ + if (!a) return false; \ + } + +/*! + Returns true if literal \a v can be assigned to property \a prop, otherwise + false. + + This test corresponds to action taken by genLiteralAssignment(). Any change + made here, must have a corresponding action in genLiteralAssigment(). +*/ +bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Value *v) +{ + const QQmlScript::Variant &value = v->value; + + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + + if (prop->core.isEnum()) { + QMetaProperty p = prop->parent->metaObject()->property(prop->index); + int enumValue; + bool ok; + if (p.isFlagType()) { + enumValue = p.enumerator().keysToValue(value.asString().toUtf8().constData(), &ok); + } else + enumValue = p.enumerator().keyToValue(value.asString().toUtf8().constData(), &ok); + + if (!ok) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration")); + + v->value = QQmlScript::Variant((double)enumValue); + return true; + } + + int type = prop->type; + + switch(type) { + case QMetaType::QVariant: + break; + case QVariant::String: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected")); + break; + case QVariant::StringList: // we expect a string literal. A string list is not a literal assignment. + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or string list expected")); + break; + case QVariant::ByteArray: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: byte array expected")); + break; + case QVariant::Url: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected")); + break; + case QVariant::RegExp: + COMPILE_EXCEPTION(v, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + break; + case QVariant::UInt: + { + bool ok = v->value.isNumber(); + if (ok) { + double n = v->value.asNumber(); + if (double(uint(n)) != n) + ok = false; + } + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: + { + bool ok = v->value.isNumber(); + if (ok) { + double n = v->value.asNumber(); + if (double(int(n)) != n) + ok = false; + } + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: + if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); + break; + case QVariant::Double: + if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); + break; + case QVariant::Color: + { + bool ok; + QQmlStringConverters::colorFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected")); + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: + { + bool ok; + QQmlStringConverters::dateFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected")); + } + break; + case QVariant::Time: + { + bool ok; + QQmlStringConverters::timeFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected")); + } + break; + case QVariant::DateTime: + { + bool ok; + QQmlStringConverters::dateTimeFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected")); + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: + case QVariant::PointF: + { + bool ok; + QQmlStringConverters::pointFFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected")); + } + break; + case QVariant::Size: + case QVariant::SizeF: + { + bool ok; + QQmlStringConverters::sizeFFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected")); + } + break; + case QVariant::Rect: + case QVariant::RectF: + { + bool ok; + QQmlStringConverters::rectFFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected")); + } + break; + case QVariant::Bool: + { + if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected")); + } + break; + case QVariant::Vector3D: + { + bool ok; + QQmlStringConverters::vector3DFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); + } + break; + case QVariant::Vector4D: + { + bool ok; + QQmlStringConverters::vector4DFromString(value.asString(), &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected")); + } + break; + default: + { + // check if assigning a literal value to a list property. + // in each case, check the singular, since an Array of the specified type + // will not go via this literal assignment codepath. + if (type == qMetaTypeId<QList<qreal> >()) { + if (!v->value.isNumber()) { + COMPILE_EXCEPTION(v, tr("Invalid property assignment: real or array of reals expected")); + } + break; + } else if (type == qMetaTypeId<QList<int> >()) { + bool ok = v->value.isNumber(); + if (ok) { + double n = v->value.asNumber(); + if (double(int(n)) != n) + ok = false; + } + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int or array of ints expected")); + break; + } else if (type == qMetaTypeId<QList<bool> >()) { + if (!v->value.isBoolean()) { + COMPILE_EXCEPTION(v, tr("Invalid property assignment: bool or array of bools expected")); + } + break; + } else if (type == qMetaTypeId<QList<QString> >()) { // we expect a string literal. A string list is not a literal assignment. + if (!v->value.isString()) { + COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (type == qMetaTypeId<QList<QUrl> >()) { + if (!v->value.isString()) { + COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected")); + } + break; + } + + // otherwise, check for existence of string converter to custom type + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type); + if (!converter) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QVariant::typeToName((QVariant::Type)type)))); + } + break; + } + return true; +} + +static QUrl urlFromUserString(const QString &data) +{ + QUrl u; + // Preserve any valid percent-encoded octets supplied by the source + u.setEncodedUrl(data.toUtf8(), QUrl::TolerantMode); + return u; +} + +/*! + Generate a store instruction for assigning literal \a v to property \a prop. + + Any literal assignment that is approved in testLiteralAssignment() must have + a corresponding action in this method. +*/ +void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Value *v) +{ + if (prop->core.isEnum()) { + Q_ASSERT(v->value.isNumber()); + // Preresolved value + int value = (int)v->value.asNumber(); + + Instruction::StoreInteger instr; + instr.propertyIndex = prop->index; + instr.value = value; + output->addInstruction(instr); + return; + } + + int type = prop->type; + switch(type) { + case QMetaType::QVariant: + { + if (v->value.isNumber()) { + double n = v->value.asNumber(); + if (double(int(n)) == n) { + if (prop->core.isVMEProperty()) { + Instruction::StoreVarInteger instr; + instr.propertyIndex = prop->index; + instr.value = int(n); + output->addInstruction(instr); + } else { + Instruction::StoreVariantInteger instr; + instr.propertyIndex = prop->index; + instr.value = int(n); + output->addInstruction(instr); + } + } else { + if (prop->core.isVMEProperty()) { + Instruction::StoreVarDouble instr; + instr.propertyIndex = prop->index; + instr.value = n; + output->addInstruction(instr); + } else { + Instruction::StoreVariantDouble instr; + instr.propertyIndex = prop->index; + instr.value = n; + output->addInstruction(instr); + } + } + } else if (v->value.isBoolean()) { + if (prop->core.isVMEProperty()) { + Instruction::StoreVarBool instr; + instr.propertyIndex = prop->index; + instr.value = v->value.asBoolean(); + output->addInstruction(instr); + } else { + Instruction::StoreVariantBool instr; + instr.propertyIndex = prop->index; + instr.value = v->value.asBoolean(); + output->addInstruction(instr); + } + } else { + if (prop->core.isVMEProperty()) { + Instruction::StoreVar instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + } else { + Instruction::StoreVariant instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + } + } + } + break; + case QVariant::String: + { + Instruction::StoreString instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + } + break; + case QVariant::StringList: + { + Instruction::StoreStringList instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + } + break; + case QVariant::ByteArray: + { + Instruction::StoreByteArray instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForByteArray(v->value.asString().toLatin1()); + output->addInstruction(instr); + } + break; + case QVariant::Url: + { + Instruction::StoreUrl instr; + QString string = v->value.asString(); + QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(urlFromUserString(string)); + instr.propertyIndex = prop->index; + instr.value = output->indexForUrl(u); + output->addInstruction(instr); + } + break; + case QVariant::UInt: + { + Instruction::StoreInteger instr; + instr.propertyIndex = prop->index; + instr.value = uint(v->value.asNumber()); + output->addInstruction(instr); + } + break; + case QVariant::Int: + { + Instruction::StoreInteger instr; + instr.propertyIndex = prop->index; + instr.value = int(v->value.asNumber()); + output->addInstruction(instr); + } + break; + case QMetaType::Float: + { + Instruction::StoreFloat instr; + instr.propertyIndex = prop->index; + instr.value = float(v->value.asNumber()); + output->addInstruction(instr); + } + break; + case QVariant::Double: + { + Instruction::StoreDouble instr; + instr.propertyIndex = prop->index; + instr.value = v->value.asNumber(); + output->addInstruction(instr); + } + break; + case QVariant::Color: + { + Instruction::StoreColor instr; + QColor c = QQmlStringConverters::colorFromString(v->value.asString()); + instr.propertyIndex = prop->index; + instr.value = c.rgba(); + output->addInstruction(instr); + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: + { + Instruction::StoreDate instr; + QDate d = QQmlStringConverters::dateFromString(v->value.asString()); + instr.propertyIndex = prop->index; + instr.value = d.toJulianDay(); + output->addInstruction(instr); + } + break; + case QVariant::Time: + { + Instruction::StoreTime instr; + QTime time = QQmlStringConverters::timeFromString(v->value.asString()); + instr.propertyIndex = prop->index; + Q_ASSERT(sizeof(instr.time) == sizeof(QTime)); + ::memcpy(&instr.time, &time, sizeof(QTime)); + output->addInstruction(instr); + } + break; + case QVariant::DateTime: + { + Instruction::StoreDateTime instr; + QDateTime dateTime = QQmlStringConverters::dateTimeFromString(v->value.asString()); + QTime time = dateTime.time(); + instr.propertyIndex = prop->index; + instr.date = dateTime.date().toJulianDay(); + Q_ASSERT(sizeof(instr.time) == sizeof(QTime)); + ::memcpy(&instr.time, &time, sizeof(QTime)); + output->addInstruction(instr); + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: + { + Instruction::StorePoint instr; + bool ok; + QPoint point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok).toPoint(); + instr.propertyIndex = prop->index; + instr.point.xp = point.x(); + instr.point.yp = point.y(); + output->addInstruction(instr); + } + break; + case QVariant::PointF: + { + Instruction::StorePointF instr; + bool ok; + QPointF point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok); + instr.propertyIndex = prop->index; + instr.point.xp = point.x(); + instr.point.yp = point.y(); + output->addInstruction(instr); + } + break; + case QVariant::Size: + { + Instruction::StoreSize instr; + bool ok; + QSize size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok).toSize(); + instr.propertyIndex = prop->index; + instr.size.wd = size.width(); + instr.size.ht = size.height(); + output->addInstruction(instr); + } + break; + case QVariant::SizeF: + { + Instruction::StoreSizeF instr; + bool ok; + QSizeF size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok); + instr.propertyIndex = prop->index; + instr.size.wd = size.width(); + instr.size.ht = size.height(); + output->addInstruction(instr); + } + break; + case QVariant::Rect: + { + Instruction::StoreRect instr; + bool ok; + QRect rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok).toRect(); + instr.propertyIndex = prop->index; + instr.rect.x1 = rect.left(); + instr.rect.y1 = rect.top(); + instr.rect.x2 = rect.right(); + instr.rect.y2 = rect.bottom(); + output->addInstruction(instr); + } + break; + case QVariant::RectF: + { + Instruction::StoreRectF instr; + bool ok; + QRectF rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok); + instr.propertyIndex = prop->index; + instr.rect.xp = rect.left(); + instr.rect.yp = rect.top(); + instr.rect.w = rect.width(); + instr.rect.h = rect.height(); + output->addInstruction(instr); + } + break; + case QVariant::Bool: + { + Instruction::StoreBool instr; + bool b = v->value.asBoolean(); + instr.propertyIndex = prop->index; + instr.value = b; + output->addInstruction(instr); + } + break; + case QVariant::Vector3D: + { + Instruction::StoreVector3D instr; + bool ok; + QVector3D vector = QQmlStringConverters::vector3DFromString(v->value.asString(), &ok); + instr.propertyIndex = prop->index; + instr.vector.xp = vector.x(); + instr.vector.yp = vector.y(); + instr.vector.zp = vector.z(); + output->addInstruction(instr); + } + break; + case QVariant::Vector4D: + { + Instruction::StoreVector4D instr; + bool ok; + QVector4D vector = QQmlStringConverters::vector4DFromString(v->value.asString(), &ok); + instr.propertyIndex = prop->index; + instr.vector.xp = vector.x(); + instr.vector.yp = vector.y(); + instr.vector.zp = vector.z(); + instr.vector.wp = vector.w(); + output->addInstruction(instr); + } + break; + default: + { + // generate single literal value assignment to a list property if required + if (type == qMetaTypeId<QList<qreal> >()) { + Instruction::StoreDoubleQList instr; + instr.propertyIndex = prop->index; + instr.value = v->value.asNumber(); + output->addInstruction(instr); + break; + } else if (type == qMetaTypeId<QList<int> >()) { + Instruction::StoreIntegerQList instr; + instr.propertyIndex = prop->index; + instr.value = int(v->value.asNumber()); + output->addInstruction(instr); + break; + } else if (type == qMetaTypeId<QList<bool> >()) { + Instruction::StoreBoolQList instr; + bool b = v->value.asBoolean(); + instr.propertyIndex = prop->index; + instr.value = b; + output->addInstruction(instr); + break; + } else if (type == qMetaTypeId<QList<QUrl> >()) { + Instruction::StoreUrlQList instr; + QString string = v->value.asString(); + QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(urlFromUserString(string)); + instr.propertyIndex = prop->index; + instr.value = output->indexForUrl(u); + output->addInstruction(instr); + break; + } else if (type == qMetaTypeId<QList<QString> >()) { + Instruction::StoreStringQList instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + break; + } + + // otherwise, generate custom type literal assignment + Instruction::AssignCustomType instr; + instr.propertyIndex = prop->index; + instr.primitive = output->indexForString(v->value.asString()); + instr.type = type; + output->addInstruction(instr); + } + break; + } +} + +/*! + Resets data by clearing the lists that the QQmlCompiler modifies. +*/ +void QQmlCompiler::reset(QQmlCompiledData *data) +{ + data->types.clear(); + data->primitives.clear(); + data->datas.clear(); + data->bytecode.resize(0); +} + +/*! + Compile \a unit, and store the output in \a out. \a engine is the QQmlEngine + with which the QQmlCompiledData will be associated. + + Returns true on success, false on failure. On failure, the compile errors + are available from errors(). + + If the environment variant QML_COMPILER_DUMP is set + (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr + on a successful compiler. +*/ +bool QQmlCompiler::compile(QQmlEngine *engine, + QQmlTypeData *unit, + QQmlCompiledData *out) +{ + exceptions.clear(); + + Q_ASSERT(out); + reset(out); + + QQmlScript::Object *root = unit->parser().tree(); + Q_ASSERT(root); + + this->engine = engine; + this->enginePrivate = QQmlEnginePrivate::get(engine); + this->unit = unit; + this->unitRoot = root; + this->output = out; + + // Compile types + const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); + QList<QQmlScript::TypeReference *> referencedTypes = unit->parser().referencedTypes(); + + for (int ii = 0; ii < resolvedTypes.count(); ++ii) { + QQmlCompiledData::TypeReference ref; + + const QQmlTypeData::TypeReference &tref = resolvedTypes.at(ii); + QQmlScript::TypeReference *parserRef = referencedTypes.at(ii); + + if (tref.type) { + ref.type = tref.type; + if (!ref.type->isCreatable()) { + QString err = ref.type->noCreationReason(); + if (err.isEmpty()) + err = tr( "Element is not creatable."); + COMPILE_EXCEPTION(parserRef->refObjects.first(), err); + } + + if (ref.type->containsRevisionedAttributes()) { + QQmlError cacheError; + ref.typePropertyCache = enginePrivate->cache(ref.type, resolvedTypes.at(ii).minorVersion, + cacheError); + if (!ref.typePropertyCache) + COMPILE_EXCEPTION(parserRef->refObjects.first(), cacheError.description()); + ref.typePropertyCache->addref(); + } + + } else if (tref.typeData) { + ref.component = tref.typeData->compiledData(); + } + ref.className = parserRef->name; + out->types << ref; + } + + compileTree(root); + + if (!isError()) { + if (compilerDump()) + out->dumpInstructions(); + if (componentStats) + dumpStats(); + Q_ASSERT(out->rootPropertyCache); + } else { + reset(out); + } + + compileState = 0; + output = 0; + this->engine = 0; + this->enginePrivate = 0; + this->unit = 0; + this->cachedComponentTypeRef = -1; + this->cachedTranslationContextIndex = -1; + this->unitRoot = 0; + + return !isError(); +} + +void QQmlCompiler::compileTree(QQmlScript::Object *tree) +{ + compileState = pool->New<ComponentCompileState>(); + + compileState->root = tree; + if (componentStats) + componentStats->componentStat.lineNumber = tree->location.start.line; + + // We generate the importCache before we build the tree so that + // it can be used in the binding compiler. Given we "expect" the + // QML compilation to succeed, this isn't a waste. + output->importCache = new QQmlTypeNameCache(); + foreach (const QString &ns, unit->namespaces()) { + output->importCache->add(ns); + } + + int scriptIndex = 0; + foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) { + QString qualifier = script.qualifier; + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex); + qualifier = qualifier.mid(lastDotIndex+1); + } + + output->importCache->add(qualifier, scriptIndex++, enclosingNamespace); + } + + unit->imports().populateCache(output->importCache, engine); + + if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) + return; + + Instruction::Init init; + init.bindingsSize = compileState->totalBindingsCount; + init.parserStatusSize = compileState->parserStatusCount; + init.contextCache = genContextCache(); + init.objectStackSize = compileState->objectDepth.maxDepth(); + init.listStackSize = compileState->listDepth.maxDepth(); + if (compileState->compiledBindingData.isEmpty()) + init.compiledBinding = -1; + else + init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData); + output->addInstruction(init); + + foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) { + Instruction::StoreImportedScript import; + import.value = output->scripts.count(); + + QQmlScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + output->scripts << scriptData; + output->addInstruction(import); + } + + if (!compileState->v8BindingProgram.isEmpty()) { + Instruction::InitV8Bindings bindings; + int index = output->programs.count(); + + typedef QQmlCompiledData::V8Program V8Program; + output->programs.append(V8Program(compileState->v8BindingProgram, output)); + + bindings.programIndex = index; + bindings.line = compileState->v8BindingProgramLine; + output->addInstruction(bindings); + } + + genObject(tree); + + Instruction::SetDefault def; + output->addInstruction(def); + + Instruction::Done done; + output->addInstruction(done); + + Q_ASSERT(tree->metatype); + + if (tree->metadata.isEmpty()) { + output->root = tree->metatype; + } else { + static_cast<QMetaObject &>(output->rootData) = *tree->metaObject(); + output->root = &output->rootData; + } + if (!tree->metadata.isEmpty()) + enginePrivate->registerCompositeType(output); +} + +static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string) +{ + for (int ii = 0; ii < list.count(); ++ii) + if (string == list.at(ii)) + return true; + + return false; +} + +bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ctxt) +{ + if (componentStats) + componentStats->componentStat.objects++; + + Q_ASSERT (obj->type != -1); + const QQmlCompiledData::TypeReference &tr = output->types.at(obj->type); + obj->metatype = tr.metaObject(); + + if (tr.type) + obj->typeName = tr.type->qmlTypeName(); + + // This object is a "Component" element + if (tr.type && obj->metatype == &QQmlComponent::staticMetaObject) { + COMPILE_CHECK(buildComponent(obj, ctxt)); + return true; + } + + if (tr.component) { + typedef QQmlInstruction I; + const I *init = ((const I *)tr.component->bytecode.constData()); + Q_ASSERT(init && tr.component->instructionType(init) == QQmlInstruction::Init); + + // Adjust stack depths to include nested components + compileState->objectDepth.pushPop(init->init.objectStackSize); + compileState->listDepth.pushPop(init->init.listStackSize); + compileState->parserStatusCount += init->init.parserStatusSize; + compileState->totalBindingsCount += init->init.bindingsSize; + } + + compileState->objectDepth.push(); + + // Object instantiations reset the binding context + BindingContext objCtxt(obj); + + // Create the synthesized meta object, ignoring aliases + COMPILE_CHECK(checkDynamicMeta(obj)); + COMPILE_CHECK(mergeDynamicMetaProperties(obj)); + COMPILE_CHECK(buildDynamicMeta(obj, IgnoreAliases)); + + // Find the native type and check for the QQmlParserStatus interface + QQmlType *type = toQmlType(obj); + Q_ASSERT(type); + obj->parserStatusCast = type->parserStatusCast(); + if (obj->parserStatusCast != -1) + compileState->parserStatusCount++; + + // Check if this is a custom parser type. Custom parser types allow + // assignments to non-existent properties. These assignments are then + // compiled by the type. + bool isCustomParser = output->types.at(obj->type).type && + output->types.at(obj->type).type->customParser() != 0; + QList<QQmlCustomParserProperty> customProps; + + // Fetch the list of deferred properties + QStringList deferredList = deferredProperties(obj); + + // Must do id property first. This is to ensure that the id given to any + // id reference created matches the order in which the objects are + // instantiated + for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { + if (prop->name() == id_string) { + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + break; + } + } + + // Merge + Property *defaultProperty = 0; + Property *skipProperty = 0; + if (obj->defaultProperty) { + defaultProperty = obj->defaultProperty; + + Property *explicitProperty = 0; + + const QMetaObject *mo = obj->metatype; + int idx = mo->indexOfClassInfo("DefaultProperty"); + if (idx != -1) { + QMetaClassInfo info = mo->classInfo(idx); + const char *p = info.value(); + if (p) { + int plen = 0; + char ord = 0; + while (char c = p[plen++]) { ord |= c; }; + --plen; + + if (ord & 0x80) { + // Utf8 - unoptimal, but seldom hit + QString *s = pool->NewString(QString::fromUtf8(p, plen)); + QHashedStringRef r(*s); + + if (obj->propertiesHashField.test(r.hash())) { + for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { + if (ep->name() == r) { + explicitProperty = ep; + break; + } + } + } + + if (!explicitProperty) + defaultProperty->setName(r); + + } else { + QHashedCStringRef r(p, plen); + + if (obj->propertiesHashField.test(r.hash())) { + for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { + if (ep->name() == r) { + explicitProperty = ep; + break; + } + } + } + + if (!explicitProperty) { + // Set the default property name + QChar *buffer = pool->NewRawArray<QChar>(r.length()); + r.writeUtf16(buffer); + defaultProperty->setName(QHashedStringRef(buffer, r.length(), r.hash())); + } + } + } + } + + if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) { + + skipProperty = explicitProperty; // We merge the values into defaultProperty + + // Find the correct insertion point + Value *insertPos = 0; + + for (Value *v = defaultProperty->values.first(); v; v = Property::ValueList::next(v)) { + if (!(v->location.start < explicitProperty->values.first()->location.start)) + break; + insertPos = v; + } + + defaultProperty->values.insertAfter(insertPos, explicitProperty->values); + } + } + + QQmlCustomParser *cp = 0; + if (isCustomParser) + cp = output->types.at(obj->type).type->customParser(); + + // Build all explicit properties specified + for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { + + if (prop == skipProperty) + continue; + if (prop->name() == id_string) + continue; + + bool canDefer = false; + if (isCustomParser) { + if (doesPropertyExist(prop, obj) && + (!(cp->flags() & QQmlCustomParser::AcceptsAttachedProperties) || + !isAttachedPropertyName(prop->name()))) { + int ids = compileState->ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState->ids.count(); + } else if (isSignalPropertyName(prop->name()) && + (cp->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); + } else { + customProps << QQmlCustomParserNodePrivate::fromProperty(prop); + } + } else { + if (isSignalPropertyName(prop->name())) { + COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); + } else { + int ids = compileState->ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState->ids.count(); + } + } + + if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name())) + prop->isDeferred = true; + + } + + // Build the default property + if (defaultProperty) { + Property *prop = defaultProperty; + + bool canDefer = false; + if (isCustomParser) { + if (doesPropertyExist(prop, obj)) { + int ids = compileState->ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState->ids.count(); + } else { + customProps << QQmlCustomParserNodePrivate::fromProperty(prop); + } + } else { + int ids = compileState->ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState->ids.count(); + } + + if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name())) + prop->isDeferred = true; + } + + // Compile custom parser parts + if (isCustomParser && !customProps.isEmpty()) { + cp->clearErrors(); + cp->compiler = this; + cp->object = obj; + obj->custom = cp->compile(customProps); + cp->compiler = 0; + cp->object = 0; + foreach (QQmlError err, cp->errors()) { + err.setUrl(output->url); + exceptions << err; + } + } + + compileState->objectDepth.pop(); + + return true; +} + +void QQmlCompiler::genObject(QQmlScript::Object *obj) +{ + QQmlCompiledData::TypeReference &tr = output->types[obj->type]; + if (tr.type && obj->metatype == &QQmlComponent::staticMetaObject) { + genComponent(obj); + return; + } + + // Create the object + if (obj->custom.isEmpty() && output->types.at(obj->type).type && + !output->types.at(obj->type).type->isExtendedType() && obj != compileState->root) { + + Instruction::CreateSimpleObject create; + create.create = output->types.at(obj->type).type->createFunction(); + create.typeSize = output->types.at(obj->type).type->createSize(); + create.type = obj->type; + create.line = obj->location.start.line; + create.column = obj->location.start.column; + output->addInstruction(create); + + } else { + + if (output->types.at(obj->type).type) { + Instruction::CreateCppObject create; + create.line = obj->location.start.line; + create.column = obj->location.start.column; + create.data = -1; + if (!obj->custom.isEmpty()) + create.data = output->indexForByteArray(obj->custom); + create.type = obj->type; + create.isRoot = (compileState->root == obj); + output->addInstruction(create); + } else { + Instruction::CreateQMLObject create; + create.type = obj->type; + create.isRoot = (compileState->root == obj); + + if (!obj->bindingBitmask.isEmpty()) { + Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); + create.bindingBits = output->indexForByteArray(obj->bindingBitmask); + } else { + create.bindingBits = -1; + } + output->addInstruction(create); + + Instruction::CompleteQMLObject complete; + complete.line = obj->location.start.line; + complete.column = obj->location.start.column; + complete.isRoot = (compileState->root == obj); + output->addInstruction(complete); + } + } + + // Setup the synthesized meta object if necessary + if (!obj->metadata.isEmpty()) { + Instruction::StoreMetaObject meta; + meta.data = output->indexForByteArray(obj->metadata); + meta.aliasData = output->indexForByteArray(obj->synthdata); + meta.propertyCache = output->propertyCaches.count(); + + QQmlPropertyCache *propertyCache = obj->synthCache; + Q_ASSERT(propertyCache); + propertyCache->addref(); + + // Add flag for alias properties + if (!obj->synthdata.isEmpty()) { + const QQmlVMEMetaData *vmeMetaData = + reinterpret_cast<const QQmlVMEMetaData *>(obj->synthdata.constData()); + for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) { + int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii; + QQmlPropertyData *data = propertyCache->property(index); + data->setFlags(data->getFlags() | QQmlPropertyData::IsAlias); + } + } + + if (obj == unitRoot) { + propertyCache->addref(); + output->rootPropertyCache = propertyCache; + } + + output->propertyCaches << propertyCache; + output->addInstruction(meta); + } else if (obj == unitRoot) { + output->rootPropertyCache = tr.createPropertyCache(engine); + output->rootPropertyCache->addref(); + } + + // Set the object id + if (!obj->id.isEmpty()) { + Instruction::SetId id; + id.value = output->indexForString(obj->id); + id.index = obj->idIndex; + output->addInstruction(id); + } + + // Begin the class + if (tr.type && obj->parserStatusCast != -1) { + Instruction::BeginObject begin; + begin.castValue = obj->parserStatusCast; + output->addInstruction(begin); + } + + genObjectBody(obj); +} + +void QQmlCompiler::genObjectBody(QQmlScript::Object *obj) +{ + for (Property *prop = obj->scriptStringProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + Q_ASSERT(prop->scriptStringScope != -1); + const QString &script = prop->values.first()->value.asScript(); + Instruction::StoreScriptString ss; + ss.propertyIndex = prop->index; + ss.value = output->indexForString(script); + ss.scope = prop->scriptStringScope; +// ss.bindingId = rewriteBinding(script, prop->name()); + ss.bindingId = rewriteBinding(prop->values.first()->value, QString()); // XXX + ss.line = prop->location.start.line; + ss.column = prop->location.start.column; + output->addInstruction(ss); + } + + bool seenDefer = false; + for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + if (prop->isDeferred) { + seenDefer = true; + continue; + } + if (!prop->isAlias) + genValueProperty(prop, obj); + } + if (seenDefer) { + Instruction::Defer defer; + defer.deferCount = 0; + int deferIdx = output->addInstruction(defer); + int nextInstructionIndex = output->nextInstructionIndex(); + + Instruction::DeferInit dinit; + // XXX - these are now massive over allocations + dinit.bindingsSize = compileState->totalBindingsCount; + dinit.parserStatusSize = compileState->parserStatusCount; + dinit.objectStackSize = compileState->objectDepth.maxDepth(); + dinit.listStackSize = compileState->listDepth.maxDepth(); + output->addInstruction(dinit); + + for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + if (!prop->isDeferred) + continue; + genValueProperty(prop, obj); + } + + Instruction::Done done; + output->addInstruction(done); + + output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex; + } + + for (Property *prop = obj->signalProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + + QQmlScript::Value *v = prop->values.first(); + + if (v->type == Value::SignalObject) { + + genObject(v->object); + + Instruction::AssignSignalObject assign; + assign.line = v->location.start.line; + assign.signal = output->indexForString(prop->name().toString()); + output->addInstruction(assign); + + } else if (v->type == Value::SignalExpression) { + + Instruction::StoreSignal store; + store.signalIndex = prop->index; + const QString &rewrite = rewriteSignalHandler(v->value, prop->name().toString()); + store.value = output->indexForByteArray(rewrite.toUtf8()); + store.context = v->signalExpressionContextStack; + store.line = v->location.start.line; + store.column = v->location.start.column; + output->addInstruction(store); + + } + + } + + for (Property *prop = obj->attachedProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + Instruction::FetchAttached fetch; + fetch.id = prop->index; + fetch.line = prop->location.start.line; + output->addInstruction(fetch); + + genObjectBody(prop->value); + + Instruction::PopFetchedObject pop; + output->addInstruction(pop); + } + + for (Property *prop = obj->groupedProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + Instruction::FetchObject fetch; + fetch.property = prop->index; + fetch.line = prop->location.start.line; + output->addInstruction(fetch); + + if (!prop->value->metadata.isEmpty()) { + Instruction::StoreMetaObject meta; + meta.data = output->indexForByteArray(prop->value->metadata); + meta.aliasData = output->indexForByteArray(prop->value->synthdata); + meta.propertyCache = -1; + output->addInstruction(meta); + } + + genObjectBody(prop->value); + + Instruction::PopFetchedObject pop; + output->addInstruction(pop); + } + + for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + if (!prop->isAlias) + genValueTypeProperty(obj, prop); + } + + for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + if (prop->isDeferred) + continue; + if (prop->isAlias) + genValueProperty(prop, obj); + } + + for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) { + if (prop->isAlias) + genValueTypeProperty(obj, prop); + } +} + +void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *prop) +{ + Instruction::FetchValueType fetch; + fetch.property = prop->index; + fetch.type = prop->type; + fetch.bindingSkipList = 0; + + if (obj->type == -1 || output->types.at(obj->type).component) { + // We only have to do this if this is a composite type. If it is a builtin + // type it can't possibly already have bindings that need to be cleared. + for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) { + if (!vprop->values.isEmpty()) { + Q_ASSERT(vprop->index >= 0 && vprop->index < 32); + fetch.bindingSkipList |= (1 << vprop->index); + } + } + } + + output->addInstruction(fetch); + + for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) { + genPropertyAssignment(vprop, prop->value, prop); + } + + Instruction::PopValueType pop; + pop.property = prop->index; + pop.type = prop->type; + pop.bindingSkipList = 0; + output->addInstruction(pop); +} + +void QQmlCompiler::genComponent(QQmlScript::Object *obj) +{ + QQmlScript::Object *root = obj->defaultProperty->values.first()->object; + Q_ASSERT(root); + + Instruction::CreateComponent create; + create.line = root->location.start.line; + create.column = root->location.start.column; + create.endLine = root->location.end.line; + create.isRoot = (compileState->root == obj); + int createInstruction = output->addInstruction(create); + int nextInstructionIndex = output->nextInstructionIndex(); + + ComponentCompileState *oldCompileState = compileState; + compileState = componentState(root); + + Instruction::Init init; + init.bindingsSize = compileState->totalBindingsCount; + init.parserStatusSize = compileState->parserStatusCount; + init.contextCache = genContextCache(); + init.objectStackSize = compileState->objectDepth.maxDepth(); + init.listStackSize = compileState->listDepth.maxDepth(); + if (compileState->compiledBindingData.isEmpty()) + init.compiledBinding = -1; + else + init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData); + output->addInstruction(init); + + if (!compileState->v8BindingProgram.isEmpty()) { + Instruction::InitV8Bindings bindings; + int index = output->programs.count(); + + typedef QQmlCompiledData::V8Program V8Program; + output->programs.append(V8Program(compileState->v8BindingProgram, output)); + + bindings.programIndex = index; + bindings.line = compileState->v8BindingProgramLine; + output->addInstruction(bindings); + } + + genObject(root); + + Instruction::SetDefault def; + output->addInstruction(def); + + Instruction::Done done; + output->addInstruction(done); + + output->instruction(createInstruction)->createComponent.count = + output->nextInstructionIndex() - nextInstructionIndex; + + compileState = oldCompileState; + + if (!obj->id.isEmpty()) { + Instruction::SetId id; + id.value = output->indexForString(obj->id); + id.index = obj->idIndex; + output->addInstruction(id); + } + + if (obj == unitRoot) { + output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine); + output->rootPropertyCache->addref(); + } +} + +bool QQmlCompiler::buildComponent(QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + // The special "Component" element can only have the id property and a + // default property, that actually defines the component's tree + + compileState->objectDepth.push(); + + // Find, check and set the "id" property (if any) + Property *idProp = 0; + if (obj->properties.isMany() || + (obj->properties.isOne() && obj->properties.first()->name() != id_string)) + COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id")); + + if (!obj->properties.isEmpty()) + idProp = obj->properties.first(); + + if (idProp) { + if (idProp->value || idProp->values.isMany() || idProp->values.first()->object) + COMPILE_EXCEPTION(idProp, tr("Invalid component id specification")); + COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive())) + + QString idVal = idProp->values.first()->primitive(); + + if (compileState->ids.value(idVal)) + COMPILE_EXCEPTION(idProp, tr("id is not unique")); + + obj->id = idVal; + addId(idVal, obj); + } + + // Check the Component tree is well formed + if (obj->defaultProperty && + (obj->defaultProperty->value || obj->defaultProperty->values.isMany() || + (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object))) + COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); + + if (!obj->dynamicProperties.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); + if (!obj->dynamicSignals.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); + if (!obj->dynamicSlots.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); + + QQmlScript::Object *root = 0; + if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty()) + root = obj->defaultProperty->values.first()->object; + + if (!root) + COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); + + // Build the component tree + COMPILE_CHECK(buildComponentFromRoot(root, ctxt)); + + compileState->objectDepth.pop(); + + return true; +} + +bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + ComponentCompileState *oldComponentCompileState = compileState; + compileState = pool->New<ComponentCompileState>(); + compileState->root = obj; + compileState->nested = true; + + if (componentStats) { + ComponentStat oldComponentStat = componentStats->componentStat; + + componentStats->componentStat = ComponentStat(); + componentStats->componentStat.lineNumber = obj->location.start.line; + + if (obj) + COMPILE_CHECK(buildObject(obj, ctxt)); + + COMPILE_CHECK(completeComponentBuild()); + + componentStats->componentStat = oldComponentStat; + } else { + if (obj) + COMPILE_CHECK(buildObject(obj, ctxt)); + + COMPILE_CHECK(completeComponentBuild()); + } + + compileState = oldComponentCompileState; + + return true; +} + + +// Build a sub-object. A sub-object is one that was not created directly by +// QML - such as a grouped property object, or an attached object. Sub-object's +// can't have an id, involve a custom parser, have attached properties etc. +bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt) +{ + Q_ASSERT(obj->metatype); + Q_ASSERT(!obj->defaultProperty); + Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding + // sub-context + + for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { + if (isSignalPropertyName(prop->name())) { + COMPILE_CHECK(buildSignal(prop, obj, ctxt)); + } else { + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); + } + } + + return true; +} + +int QQmlCompiler::componentTypeRef() +{ + if (cachedComponentTypeRef == -1) { + QQmlType *t = QQmlMetaType::qmlType(Component_import_string,1,0); + for (int ii = output->types.count() - 1; ii >= 0; --ii) { + if (output->types.at(ii).type == t) { + cachedComponentTypeRef = ii; + return ii; + } + } + QQmlCompiledData::TypeReference ref; + ref.className = Component_string; + ref.type = t; + output->types << ref; + cachedComponentTypeRef = output->types.count() - 1; + } + return cachedComponentTypeRef; +} + +int QQmlCompiler::translationContextIndex() +{ + if (cachedTranslationContextIndex == -1) { + // This code must match that in the qsTr() implementation + const QString &path = output->name; + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) : + QString(); + QByteArray contextUtf8 = context.toUtf8(); + cachedTranslationContextIndex = output->indexForByteArray(contextUtf8); + } + return cachedTranslationContextIndex; +} + +bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(obj->metaObject()); + + const QHashedStringRef &propName = prop->name(); + + Q_ASSERT(propName.startsWith(on_string)); + QString name = propName.mid(2, -1).toString(); + + // 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 < name.size(); ++firstAlphaIndex) { + if (name.at(firstAlphaIndex).isUpper()) { + name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower(); + break; + } + } + + bool notInRevision = false; + + QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision); + + if (sig == 0) { + + if (notInRevision && 0 == property(obj, propName, 0)) { + Q_ASSERT(obj->type != -1); + const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); + const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type); + if (type.type) { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion)); + } else { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString())); + } + } + + // If the "on<Signal>" name doesn't resolve into a signal, try it as a + // property. + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); + + } else { + + if (prop->value || !prop->values.isOne()) + COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment")); + + prop->index = sig->coreIndex; + prop->core = *sig; + + obj->addSignalProperty(prop); + + if (prop->values.first()->object) { + COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt)); + prop->values.first()->type = Value::SignalObject; + } else { + prop->values.first()->type = Value::SignalExpression; + + if (!prop->values.first()->value.isScript()) + COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)")); + + QString script = prop->values.first()->value.asScript().trimmed(); + if (script.isEmpty()) + COMPILE_EXCEPTION(prop, tr("Empty signal assignment")); + + prop->values.first()->signalExpressionContextStack = ctxt.stack; + } + } + + return true; +} + + +/*! + Returns true if (value) property \a prop exists on obj, false otherwise. +*/ +bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop, + QQmlScript::Object *obj) +{ + if (prop->name().isEmpty()) + return false; + if(isAttachedPropertyName(prop->name()) || prop->name() == id_string) + return true; + + return property(obj, prop->name()) != 0; +} + +bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + if (prop->isEmpty()) + COMPILE_EXCEPTION(prop, tr("Empty property assignment")); + + const QMetaObject *metaObject = obj->metaObject(); + Q_ASSERT(metaObject); + + if (isAttachedPropertyName(prop->name())) { + // Setup attached property data + + if (ctxt.isSubContext()) { + // Attached properties cannot be used on sub-objects. Sub-objects + // always exist in a binding sub-context, which is what we test + // for here. + COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here")); + } + + QQmlType *type = 0; + QQmlImportedNamespace *typeNamespace = 0; + unit->imports().resolveType(prop->name().toString(), &type, 0, 0, 0, &typeNamespace); + + if (typeNamespace) { + COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, + ctxt)); + return true; + } else if (!type || !type->attachedPropertiesType()) { + COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); + } + + if (!prop->value) + COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); + + Q_ASSERT(type->attachedPropertiesFunction()); + prop->index = type->attachedPropertiesId(); + prop->value->metatype = type->attachedPropertiesType(); + } else { + // Setup regular property data + bool notInRevision = false; + QQmlPropertyData *d = + prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision); + + if (d == 0 && notInRevision) { + const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); + const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type); + if (type.type) { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion)); + } else { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString())); + } + } else if (d) { + prop->index = d->coreIndex; + prop->core = *d; + } else if (prop->isDefault) { + QMetaProperty p = QQmlMetaType::defaultProperty(metaObject); + QQmlPropertyData defaultPropertyData; + defaultPropertyData.load(p, engine); + if (p.name()) + prop->setName(QLatin1String(p.name())); + prop->core = defaultPropertyData; + prop->index = prop->core.coreIndex; + } + + // We can't error here as the "id" property does not require a + // successful index resolution + if (prop->index != -1) + prop->type = prop->core.propType; + + // Check if this is an alias + if (prop->index != -1 && + prop->parent && + prop->parent->type != -1 && + output->types.at(prop->parent->type).component) { + + QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache; + if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias()) + prop->isAlias = true; + } + + if (prop->index != -1 && !prop->values.isEmpty()) + prop->parent->setBindingBit(prop->index); + } + + if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) { + + // The magic "id" behavior doesn't apply when "id" is resolved as a + // default property or to sub-objects (which are always in binding + // sub-contexts) + COMPILE_CHECK(buildIdProperty(prop, obj)); + if (prop->type == QVariant::String && + prop->values.first()->value.isString()) + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); + + } else if (isAttachedPropertyName(prop->name())) { + + COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); + + } else if (prop->index == -1) { + + if (prop->isDefault) { + COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property")); + } else { + COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString())); + } + + } else if (prop->value) { + + COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); + + } else if (prop->core.isQList()) { + + COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); + + } else if (prop->type == qMetaTypeId<QQmlScriptString>()) { + + COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt)); + + } else { + + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); + + } + + return true; +} + +bool QQmlCompiler::buildPropertyInNamespace(QQmlImportedNamespace *ns, + QQmlScript::Property *nsProp, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + if (!nsProp->value) + COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); + + for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) { + + if (!isAttachedPropertyName(prop->name())) + COMPILE_EXCEPTION(prop, tr("Not an attached property name")); + + // Setup attached property data + + QQmlType *type = 0; + unit->imports().resolveType(ns, prop->name().toString(), &type, 0, 0, 0); + + if (!type || !type->attachedPropertiesType()) + COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); + + if (!prop->value) + COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); + + Q_ASSERT(type->attachedPropertiesFunction()); + prop->index = type->index(); + prop->value->metatype = type->attachedPropertiesType(); + + COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); + } + + return true; +} + +void QQmlCompiler::genValueProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj) +{ + if (prop->core.isQList()) { + genListProperty(prop, obj); + } else { + genPropertyAssignment(prop, obj); + } +} + +void QQmlCompiler::genListProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj) +{ + int listType = enginePrivate->listType(prop->type); + + Instruction::FetchQList fetch; + fetch.property = prop->index; + bool listTypeIsInterface = QQmlMetaType::isInterface(listType); + fetch.type = listType; + output->addInstruction(fetch); + + for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) { + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + if (listTypeIsInterface) { + Instruction::AssignObjectList assign; + assign.line = prop->location.start.line; + output->addInstruction(assign); + } else { + Instruction::StoreObjectQList store; + output->addInstruction(store); + } + + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj); + + } + + } + + Instruction::PopQList pop; + output->addInstruction(pop); +} + +void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Property *valueTypeProperty) +{ + for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) { + + Q_ASSERT(v->type == Value::CreatedObject || + v->type == Value::PropertyBinding || + v->type == Value::Literal); + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + + if (QQmlMetaType::isInterface(prop->type)) { + + Instruction::StoreInterface store; + store.line = v->object->location.start.line; + store.propertyIndex = prop->index; + output->addInstruction(store); + + } else if (prop->type == QMetaType::QVariant) { + + if (prop->core.isVMEProperty()) { + Instruction::StoreVarObject store; + store.line = v->object->location.start.line; + store.propertyIndex = prop->index; + output->addInstruction(store); + } else { + Instruction::StoreVariantObject store; + store.line = v->object->location.start.line; + store.propertyIndex = prop->index; + output->addInstruction(store); + } + + + } else { + + Instruction::StoreObject store; + store.line = v->object->location.start.line; + store.propertyIndex = prop->index; + output->addInstruction(store); + + } + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj, valueTypeProperty); + + } else if (v->type == Value::Literal) { + + genLiteralAssignment(prop, v); + + } + + } + + for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) { + + Q_ASSERT(v->type == Value::ValueSource || + v->type == Value::ValueInterceptor); + + if (v->type == Value::ValueSource) { + genObject(v->object); + + Instruction::StoreValueSource store; + if (valueTypeProperty) { + store.property = genValueTypeData(prop, valueTypeProperty); + store.owner = 1; + } else { + store.property = prop->core; + store.owner = 0; + } + QQmlType *valueType = toQmlType(v->object); + store.castValue = valueType->propertyValueSourceCast(); + output->addInstruction(store); + + } else if (v->type == Value::ValueInterceptor) { + genObject(v->object); + + Instruction::StoreValueInterceptor store; + if (valueTypeProperty) { + store.property = genValueTypeData(prop, valueTypeProperty); + store.owner = 1; + } else { + store.property = prop->core; + store.owner = 0; + } + QQmlType *valueType = toQmlType(v->object); + store.castValue = valueType->propertyValueInterceptorCast(); + output->addInstruction(store); + } + + } +} + +bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj) +{ + if (prop->value || + prop->values.isMany() || + prop->values.first()->object) + COMPILE_EXCEPTION(prop, tr("Invalid use of id property")); + + QQmlScript::Value *idValue = prop->values.first(); + QString val = idValue->primitive(); + + COMPILE_CHECK(checkValidId(idValue, val)); + + if (compileState->ids.value(val)) + COMPILE_EXCEPTION(prop, tr("id is not unique")); + + prop->values.first()->type = Value::Id; + + obj->id = val; + addId(val, obj); + + return true; +} + +void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj) +{ + Q_UNUSED(id); + Q_ASSERT(!compileState->ids.value(id)); + Q_ASSERT(obj->id == id); + obj->idIndex = compileState->ids.count(); + compileState->ids.append(obj); +} + +void QQmlCompiler::addBindingReference(JSBindingReference *ref) +{ + Q_ASSERT(ref->value && !ref->value->bindingReference); + ref->value->bindingReference = ref; + compileState->totalBindingsCount++; + compileState->bindings.prepend(ref); +} + +void QQmlCompiler::saveComponentState() +{ + Q_ASSERT(compileState->root); + Q_ASSERT(compileState->root->componentCompileState == 0); + + compileState->root->componentCompileState = compileState; + + if (componentStats) + componentStats->savedComponentStats.append(componentStats->componentStat); +} + +QQmlCompilerTypes::ComponentCompileState * +QQmlCompiler::componentState(QQmlScript::Object *obj) +{ + Q_ASSERT(obj->componentCompileState); + return obj->componentCompileState; +} + +// Build attached property object. In this example, +// Text { +// GridView.row: 10 +// } +// GridView is an attached property object. +bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->value); + Q_ASSERT(prop->index != -1); // This is set in buildProperty() + + compileState->objectDepth.push(); + + obj->addAttachedProperty(prop); + + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + + compileState->objectDepth.pop(); + + return true; +} + + +// Build "grouped" properties. In this example: +// Text { +// font.pointSize: 12 +// font.family: "Helvetica" +// } +// font is a nested property. pointSize and family are not. +bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->type != 0); + Q_ASSERT(prop->index != -1); + + if (QQmlValueTypeFactory::isValueType(prop->type)) { + if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) { + + if (!prop->values.isEmpty()) { + if (prop->values.first()->location < prop->value->location) { + COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); + } else { + COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value")); + } + } + + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) { + COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + } + + + if (prop->isAlias) { + for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) { + vtProp->isAlias = true; + } + } + + COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], + prop->value, obj, ctxt.incr())); + obj->addValueTypeProperty(prop); + } else { + COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); + } + + } else { + // Load the nested property's meta type + prop->value->metatype = enginePrivate->metaObjectForType(prop->type); + if (!prop->value->metatype) + COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); + + if (!prop->values.isEmpty()) + COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign a value directly to a grouped property")); + + obj->addGroupedProperty(prop); + + compileState->objectDepth.push(); + + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + + compileState->objectDepth.pop(); + } + + return true; +} + +bool QQmlCompiler::buildValueTypeProperty(QObject *type, + QQmlScript::Object *obj, + QQmlScript::Object *baseObj, + const BindingContext &ctxt) +{ + compileState->objectDepth.push(); + + if (obj->defaultProperty) + COMPILE_EXCEPTION(obj, tr("Invalid property use")); + obj->metatype = type->metaObject(); + + for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { + + QQmlPropertyData *d = property(obj, prop->name()); + if (d == 0) + COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString())); + + prop->index = d->coreIndex; + prop->type = d->propType; + prop->core = *d; + prop->isValueTypeSubProperty = true; + + if (prop->value) + COMPILE_EXCEPTION(prop, tr("Property assignment expected")); + + if (prop->values.isMany()) { + COMPILE_EXCEPTION(prop, tr("Single property assignment expected")); + } else if (!prop->values.isEmpty()) { + QQmlScript::Value *value = prop->values.first(); + + if (value->object) { + COMPILE_EXCEPTION(prop, tr("Unexpected object assignment")); + } else if (value->value.isScript()) { + // ### Check for writability + + //optimization for <Type>.<EnumValue> enum assignments + bool isEnumAssignment = false; + + if (prop->core.isEnum()) + COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment)); + + if (isEnumAssignment) { + value->type = Value::Literal; + } else { + JSBindingReference *reference = pool->New<JSBindingReference>(); + reference->expression = value->value; + reference->property = prop; + reference->value = value; + reference->bindingContext = ctxt; + reference->bindingContext.owner++; + addBindingReference(reference); + value->type = Value::PropertyBinding; + } + } else { + COMPILE_CHECK(testLiteralAssignment(prop, value)); + value->type = Value::Literal; + } + } + + for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) { + Q_ASSERT(v->object); + + COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt)); + } + + obj->addValueProperty(prop); + } + + compileState->objectDepth.pop(); + + return true; +} + +// Build assignments to QML lists. QML lists are properties of type +// QQmlListProperty<T>. List properties can accept a list of +// objects, or a single binding. +bool QQmlCompiler::buildListProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->core.isQList()); + + compileState->listDepth.push(); + + int t = prop->type; + + obj->addValueProperty(prop); + + int listType = enginePrivate->listType(t); + bool listTypeIsInterface = QQmlMetaType::isInterface(listType); + + bool assignedBinding = false; + for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) { + if (v->object) { + v->type = Value::CreatedObject; + COMPILE_CHECK(buildObject(v->object, ctxt)); + + // We check object coercian here. We check interface assignment + // at runtime. + if (!listTypeIsInterface) { + if (!canCoerce(listType, v->object)) { + COMPILE_EXCEPTION(v, tr("Cannot assign object to list")); + } + } + + } else if (v->value.isScript()) { + if (assignedBinding) + COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists")); + + assignedBinding = true; + COMPILE_CHECK(buildBinding(v, prop, ctxt)); + v->type = Value::PropertyBinding; + } else { + COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists")); + } + } + + compileState->listDepth.pop(); + + return true; +} + +// Compiles an assignment to a QQmlScriptString property +bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + if (prop->values.isMany()) + COMPILE_EXCEPTION(prop->values.first()->nextValue, tr( "Cannot assign multiple values to a script property")); + + if (prop->values.first()->object) + COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected")); + + prop->scriptStringScope = ctxt.stack; + obj->addScriptStringProperty(prop); + + return true; +} + +// Compile regular property assignments of the form "property: <value>" +bool QQmlCompiler::buildPropertyAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const BindingContext &ctxt) +{ + obj->addValueProperty(prop); + + if (prop->values.isMany()) + COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a singular property") ); + + for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) { + if (v->object) { + + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); + + } else { + + COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); + + } + } + + for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) { + Q_ASSERT(v->object); + COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt)); + } + + return true; +} + +// Compile assigning a single object instance to a regular property +bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(v->object->type != -1); + + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + + if (QQmlMetaType::isInterface(prop->type)) { + + // Assigning an object to an interface ptr property + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + + } else if (prop->type == QMetaType::QVariant) { + + // Assigning an object to a QVariant + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + } else { + // Normally buildObject() will set this up, but we need the static + // meta object earlier to test for assignability. It doesn't matter + // that there may still be outstanding synthesized meta object changes + // on this type, as they are not relevant for assignability testing + v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); + + // We want to 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 + const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type); + + // Will be true if the assgned type inherits propertyMetaObject + bool isAssignable = false; + // Determine isAssignable value + if (propertyMetaObject) { + const QMetaObject *c = v->object->metatype; + while(c) { + isAssignable |= (QQmlPropertyPrivate::equal(c, propertyMetaObject)); + c = c->superClass(); + } + } + + if (isAssignable) { + // Simple assignment + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + } else if (propertyMetaObject == &QQmlComponent::staticMetaObject) { + // Automatic "Component" insertion + QQmlScript::Object *root = v->object; + QQmlScript::Object *component = pool->New<Object>(); + component->type = componentTypeRef(); + component->typeName = QStringLiteral("Qt/Component"); + component->metatype = &QQmlComponent::staticMetaObject; + component->location = root->location; + QQmlScript::Value *componentValue = pool->New<Value>(); + componentValue->object = root; + component->getDefaultProperty()->addValue(componentValue); + v->object = component; + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); + } else { + COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property")); + } + } + + return true; +} + +// Compile assigning a single object instance to a regular property using the "on" syntax. +// +// For example: +// Item { +// NumberAnimation on x { } +// } +bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Object *baseObj, + QQmlScript::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(v->object->type != -1); + + Q_UNUSED(obj); + + if (!prop->core.isWritable()) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + + + // Normally buildObject() will set this up, but we need the static + // meta object earlier to test for assignability. It doesn't matter + // that there may still be outstanding synthesized meta object changes + // on this type, as they are not relevant for assignability testing + v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); + + // Will be true if the assigned type inherits QQmlPropertyValueSource + bool isPropertyValue = false; + // Will be true if the assigned type inherits QQmlPropertyValueInterceptor + bool isPropertyInterceptor = false; + if (QQmlType *valueType = toQmlType(v->object)) { + isPropertyValue = valueType->propertyValueSourceCast() != -1; + isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1; + } + + if (isPropertyValue || isPropertyInterceptor) { + // Assign as a property value source + COMPILE_CHECK(buildObject(v->object, ctxt)); + + if (isPropertyInterceptor && prop->parent->synthdata.isEmpty()) + buildDynamicMeta(baseObj, ForceCreation); + v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor; + } else { + COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(v->object->typeName).arg(prop->name().toString())); + } + + return true; +} + +// Compile assigning a literal or binding to a regular property +bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + + if (v->value.isScript()) { + + //optimization for <Type>.<EnumValue> enum assignments + if (prop->core.isEnum()) { + bool isEnumAssignment = false; + COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment)); + if (isEnumAssignment) { + v->type = Value::Literal; + return true; + } + } + + // Test for other binding optimizations + if (!buildLiteralBinding(v, prop, ctxt)) + COMPILE_CHECK(buildBinding(v, prop, ctxt)); + + v->type = Value::PropertyBinding; + + } else { + + COMPILE_CHECK(testLiteralAssignment(prop, v)); + + v->type = Value::Literal; + } + + return true; +} + +bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *v, + bool *isAssignment) +{ + *isAssignment = false; + if (!prop->core.isEnum()) + return true; + + QMetaProperty mprop = obj->metaObject()->property(prop->index); + + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + + QString string = v->value.asString(); + if (!string.at(0).isUpper()) + return true; + + QStringList parts = string.split(QLatin1Char('.')); + if (parts.count() != 2) + return true; + + QString typeName = parts.at(0); + QQmlType *type = 0; + unit->imports().resolveType(typeName, &type, 0, 0, 0, 0); + + //handle enums on value types (where obj->typeName is empty) + QString objTypeName = obj->typeName; + if (objTypeName.isEmpty()) { + QQmlType *objType = toQmlType(obj); + if (objType) + objTypeName = objType->qmlTypeName(); + } + + if (!type) + return true; + + QString enumValue = parts.at(1); + int value; + bool ok; + + if (objTypeName == type->qmlTypeName()) { + // When these two match, we can short cut the search + if (mprop.isFlagType()) { + value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); + } else { + value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); + } + } else { + // Otherwise we have to search the whole type + // This matches the logic in QV8TypeWrapper + QByteArray enumName = enumValue.toUtf8(); + const QMetaObject *metaObject = type->baseMetaObject(); + ok = false; + 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; + + v->type = Value::Literal; + v->value = QQmlScript::Variant((double)value); + *isAssignment = true; + + return true; +} + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +// Similar logic to above, but not knowing target property. +int QQmlCompiler::evaluateEnum(const QByteArray& script) const +{ + int dot = script.indexOf('.'); + if (dot > 0) { + const QByteArray &scope = script.left(dot); + QQmlType *type = 0; + unit->imports().resolveType(QString::fromUtf8(script.left(dot)), &type, 0, 0, 0, 0); + if (!type && scope != "Qt") + return -1; + const QMetaObject *mo = type ? type->metaObject() : StaticQtMetaObject::get(); + const char *key = script.constData() + dot+1; + int i = mo->enumeratorCount(); + while (i--) { + bool ok; + int v = mo->enumerator(i).keyToValue(key, &ok); + if (ok) + return v; + } + } + return -1; +} + +const QMetaObject *QQmlCompiler::resolveType(const QString& name) const +{ + QQmlType *qmltype = 0; + if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0)) + return 0; + if (!qmltype) + return 0; + return qmltype->metaObject(); +} + +// similar to logic of completeComponentBuild, but also sticks data +// into primitives at the end +int QQmlCompiler::rewriteBinding(const QQmlScript::Variant& value, const QString& name) +{ + QQmlRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName(QLatin1Char('$') + name.mid(name.lastIndexOf(QLatin1Char('.')) + 1)); + + QString rewrite = rewriteBinding(value.asAST(), value.asScript(), 0); + + return output->indexForString(rewrite); +} + +QString QQmlCompiler::rewriteSignalHandler(const QQmlScript::Variant& value, const QString &name) +{ + QQmlRewrite::RewriteSignalHandler rewriteSignalHandler; + return rewriteSignalHandler(value.asAST(), value.asScript(), name); +} + +// Ensures that the dynamic meta specification on obj is valid +bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj) +{ + bool seenDefaultProperty = false; + + // We use a coarse grain, 31 bit hash to check if there are duplicates. + // Calculating the hash for the names is not a waste as we have to test + // them against the illegalNames set anyway. + QHashField propNames; + QHashField methodNames; + + // Check properties + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + const QQmlScript::Object::DynamicProperty &prop = *p; + + if (prop.isDefaultProperty) { + if (seenDefaultProperty) + COMPILE_EXCEPTION(&prop, tr("Duplicate default property")); + seenDefaultProperty = true; + } + + if (propNames.testAndSet(prop.name.hash())) { + for (Object::DynamicProperty *p2 = obj->dynamicProperties.first(); p2 != p; + p2 = obj->dynamicProperties.next(p2)) { + if (p2->name == prop.name) { + COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, + prop.nameLocation.column, + tr("Duplicate property name")); + } + } + } + + if (prop.name.at(0).isUpper()) { + COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, + prop.nameLocation.column, + tr("Property names cannot begin with an upper case letter")); + } + + if (enginePrivate->v8engine()->illegalNames().contains(prop.name)) { + COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, + prop.nameLocation.column, + tr("Illegal property name")); + } + } + + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + const QQmlScript::Object::DynamicSignal &currSig = *s; + + if (methodNames.testAndSet(currSig.name.hash())) { + for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2 != s; + s2 = obj->dynamicSignals.next(s2)) { + if (s2->name == currSig.name) + COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name")); + } + } + + if (currSig.name.at(0).isUpper()) + COMPILE_EXCEPTION(&currSig, tr("Signal names cannot begin with an upper case letter")); + if (enginePrivate->v8engine()->illegalNames().contains(currSig.name)) + COMPILE_EXCEPTION(&currSig, tr("Illegal signal name")); + } + + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + const QQmlScript::Object::DynamicSlot &currSlot = *s; + + if (methodNames.testAndSet(currSlot.name.hash())) { + for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2; + s2 = obj->dynamicSignals.next(s2)) { + if (s2->name == currSlot.name) + COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name")); + } + for (Object::DynamicSlot *s2 = obj->dynamicSlots.first(); s2 != s; + s2 = obj->dynamicSlots.next(s2)) { + if (s2->name == currSlot.name) + COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name")); + } + } + + if (currSlot.name.at(0).isUpper()) + COMPILE_EXCEPTION(&currSlot, tr("Method names cannot begin with an upper case letter")); + if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name)) + COMPILE_EXCEPTION(&currSlot, tr("Illegal method name")); + } + + return true; +} + +bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj) +{ + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { + + if (!p->defaultValue || p->type == Object::DynamicProperty::Alias) + continue; + + Property *property = 0; + if (p->isDefaultProperty) { + property = obj->getDefaultProperty(); + } else { + property = obj->getProperty(p->name); + if (!property->values.isEmpty()) + COMPILE_EXCEPTION(property, tr("Property value set multiple times")); + } + + if (p->isReadOnly) + property->isReadOnlyDeclaration = true; + + if (property->value) + COMPILE_EXCEPTION(property, tr("Invalid property nesting")); + + property->values.append(p->defaultValue->values); + } + return true; +} + +Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter) + +bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode) +{ + Q_ASSERT(obj); + Q_ASSERT(obj->metatype); + + if (mode != ForceCreation && + obj->dynamicProperties.isEmpty() && + obj->dynamicSignals.isEmpty() && + obj->dynamicSlots.isEmpty()) + return true; + + bool resolveAlias = (mode == ResolveAliases); + + const Object::DynamicProperty *defaultProperty = 0; + int aliasCount = 0; + int varPropCount = 0; + int totalPropCount = 0; + int firstPropertyVarIndex = 0; + + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + + if (p->type == Object::DynamicProperty::Alias) + aliasCount++; + if (p->type == Object::DynamicProperty::Var) + varPropCount++; + + if (p->isDefaultProperty && + (resolveAlias || p->type != Object::DynamicProperty::Alias)) + defaultProperty = p; + + if (!resolveAlias) { + // No point doing this for both the alias and non alias cases + QQmlPropertyData *d = property(obj, p->name); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + } + } + + bool buildData = resolveAlias || aliasCount == 0; + + QByteArray dynamicData; + if (buildData) { + typedef QQmlVMEMetaData VMD; + + dynamicData = QByteArray(sizeof(QQmlVMEMetaData) + + (obj->dynamicProperties.count() - aliasCount) * sizeof(VMD::PropertyData) + + obj->dynamicSlots.count() * sizeof(VMD::MethodData) + + aliasCount * sizeof(VMD::AliasData), 0); + } + + int uniqueClassId = classIndexCounter()->fetchAndAddRelaxed(1); + + QByteArray newClassName = obj->metatype->className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(uniqueClassId)); + + if (compileState->root == obj && !compileState->nested) { + QString path = output->url.path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + QByteArray::number(uniqueClassId); + } + } + + QFastMetaBuilder builder; + QFastMetaBuilder::StringRef classNameRef = builder.init(newClassName.length(), + obj->dynamicProperties.count() - (resolveAlias?0:aliasCount), + obj->dynamicSlots.count(), + obj->dynamicSignals.count() + obj->dynamicProperties.count(), + defaultProperty?1:0); + + struct TypeData { + Object::DynamicProperty::Type dtype; + int metaType; + const char *cppType; + } builtinTypes[] = { + { Object::DynamicProperty::Var, QMetaType::QVariant, "QVariant" }, + { Object::DynamicProperty::Variant, QMetaType::QVariant, "QVariant" }, + { Object::DynamicProperty::Int, QMetaType::Int, "int" }, + { Object::DynamicProperty::Bool, QMetaType::Bool, "bool" }, + { Object::DynamicProperty::Real, QMetaType::Double, "double" }, + { Object::DynamicProperty::String, QMetaType::QString, "QString" }, + { Object::DynamicProperty::Url, QMetaType::QUrl, "QUrl" }, + { Object::DynamicProperty::Color, QMetaType::QColor, "QColor" }, + { Object::DynamicProperty::Time, QMetaType::QTime, "QTime" }, + { Object::DynamicProperty::Date, QMetaType::QDate, "QDate" }, + { Object::DynamicProperty::DateTime, QMetaType::QDateTime, "QDateTime" }, + }; + static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + QFastMetaBuilder::StringRef typeRefs[builtinTypeCount]; + + // Reserve dynamic properties + if (obj->dynamicProperties.count()) { + typedef QQmlVMEMetaData VMD; + + int effectivePropertyIndex = 0; + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + + // Reserve space for name + p->nameRef = builder.newString(p->name.utf8length()); + + int propertyType = 0; + bool readonly = false; + QFastMetaBuilder::StringRef typeRef; + + if (p->type == Object::DynamicProperty::Alias) { + continue; + } else if (p->type < builtinTypeCount) { + Q_ASSERT(builtinTypes[p->type].dtype == p->type); + propertyType = builtinTypes[p->type].metaType; + if (typeRefs[p->type].isEmpty()) + typeRefs[p->type] = builder.newString(strlen(builtinTypes[p->type].cppType)); + typeRef = typeRefs[p->type]; + + } else { + Q_ASSERT(p->type == Object::DynamicProperty::CustomList || + p->type == Object::DynamicProperty::Custom); + + // XXX don't double resolve this in the case of an alias run + + QByteArray customTypeName; + QQmlType *qmltype = 0; + QString url; + if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0)) + COMPILE_EXCEPTION(p, tr("Invalid property type")); + + if (!qmltype) { + QQmlTypeData *tdata = enginePrivate->typeLoader.get(QUrl(url)); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + customTypeName = data->root->className(); + data->release(); + tdata->release(); + } else { + customTypeName = qmltype->typeName(); + } + + if (p->type == Object::DynamicProperty::Custom) { + customTypeName += '*'; + propertyType = QMetaType::QObjectStar; + } else { + readonly = true; + customTypeName = QByteArray("QQmlListProperty<") + customTypeName + QByteArray(">"); + propertyType = qMetaTypeId<QQmlListProperty<QObject> >(); + } + + p->resolvedCustomTypeName = pool->NewByteArray(customTypeName); + p->typeRef = builder.newString(customTypeName.length()); + typeRef = p->typeRef; + } + + if (p->type == Object::DynamicProperty::Var) + continue; + + if (p->isReadOnly) + readonly = true; + + if (buildData) { + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + vmd->propertyCount++; + (vmd->propertyData() + effectivePropertyIndex)->propertyType = propertyType; + } + + if (p->type < builtinTypeCount) + builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, (QMetaType::Type)propertyType, + readonly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, + effectivePropertyIndex); + else + builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, + readonly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, + effectivePropertyIndex); + + p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); + builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); + + effectivePropertyIndex++; + } + + if (varPropCount) { + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + if (buildData) + vmd->varPropertyCount = varPropCount; + firstPropertyVarIndex = effectivePropertyIndex; + totalPropCount = varPropCount + effectivePropertyIndex; + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + if (p->type == Object::DynamicProperty::Var) { + QFastMetaBuilder::StringRef typeRef = typeRefs[p->type]; + if (buildData) { + vmd->propertyCount++; + (vmd->propertyData() + effectivePropertyIndex)->propertyType = QMetaType::QVariant; + } + + builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, + QMetaType::QVariant, + p->isReadOnly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, + effectivePropertyIndex); + + p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); + builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); + + effectivePropertyIndex++; + } + } + } + + if (aliasCount) { + int aliasIndex = 0; + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + if (p->type == Object::DynamicProperty::Alias) { + if (resolveAlias) { + Q_ASSERT(buildData); + ((QQmlVMEMetaData *)dynamicData.data())->aliasCount++; + COMPILE_CHECK(compileAlias(builder, dynamicData, obj, effectivePropertyIndex, + aliasIndex, *p)); + } + // Even if we aren't resolving the alias, we need a fake signal so that the + // metaobject remains consistent across the resolve and non-resolve alias runs + p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); + builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); + effectivePropertyIndex++; + aliasIndex++; + } + } + } + } + + // Reserve default property + QFastMetaBuilder::StringRef defPropRef; + if (defaultProperty) { + defPropRef = builder.newString(strlen("DefaultProperty")); + builder.setClassInfo(0, defPropRef, defaultProperty->nameRef); + } + + // Reserve dynamic signals + int signalIndex = 0; + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + + int paramCount = s->parameterNames.count(); + + int signatureSize = s->name.utf8length() + 2 /* paren */; + int namesSize = 0; + if (paramCount) signatureSize += s->parameterTypesLength() + (paramCount - 1) /* commas */; + if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1) /* commas */; + + s->signatureRef = builder.newString(signatureSize); + if (namesSize) s->parameterNamesRef = builder.newString(namesSize); + + if (buildData) + ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; + + builder.setSignal(signalIndex + obj->dynamicProperties.count(), s->signatureRef, s->parameterNamesRef); + ++signalIndex; + } + + // Reserve dynamic slots + if (obj->dynamicSlots.count()) { + + // Allocate QVariant string + if (typeRefs[0].isEmpty()) + typeRefs[0] = builder.newString(strlen(builtinTypes[0].cppType)); + + typedef QQmlVMEMetaData VMD; + + int methodIndex = 0; + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + int paramCount = s->parameterNames.count(); + + int signatureSize = s->name.utf8length() + 2 /* paren */; + int namesSize = 0; + if (paramCount) signatureSize += (paramCount * strlen("QVariant") + (paramCount - 1)); + if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */); + + s->signatureRef = builder.newString(signatureSize); + if (namesSize) s->parameterNamesRef = builder.newString(namesSize); + + builder.setMethod(methodIndex, s->signatureRef, s->parameterNamesRef, typeRefs[0]); + + if (buildData) { + QString funcScript; + funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ + + namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */); + funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('('); + for (int jj = 0; jj < paramCount; ++jj) { + if (jj) funcScript.append(QLatin1Char(',')); + funcScript.append(QLatin1String(s->parameterNames.at(jj))); + } + funcScript += QLatin1Char(')') + s->body + QLatin1Char(')'); + + QByteArray utf8 = funcScript.toUtf8(); + VMD::MethodData methodData = { s->parameterNames.count(), 0, + utf8.length(), + s->location.start.line }; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + vmd->methodCount++; + + VMD::MethodData &md = *(vmd->methodData() + methodIndex); + md = methodData; + md.bodyOffset = dynamicData.size(); + + dynamicData.append((const char *)utf8.constData(), utf8.length()); + } + + + methodIndex++; + } + } + + // Now allocate used builtin types + for (int ii = 0; ii < builtinTypeCount; ++ii) { + if (!typeRefs[ii].isEmpty()) + typeRefs[ii].load(builtinTypes[ii].cppType); + } + + // Now allocate properties + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + + char *d = p->changedSignatureRef.data(); + p->name.writeUtf8(d); + strcpy(d + p->name.utf8length(), "Changed()"); + + if (p->type == Object::DynamicProperty::Alias && !resolveAlias) + continue; + + p->nameRef.load(p->name); + + if (p->type >= builtinTypeCount) { + Q_ASSERT(p->resolvedCustomTypeName); + p->typeRef.load(*p->resolvedCustomTypeName); + } + } + + // Allocate default property if necessary + if (defaultProperty) + strcpy(defPropRef.data(), "DefaultProperty"); + + // Now allocate signals + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + + char *d = s->signatureRef.data(); + char *d2 = s->parameterNamesRef.isEmpty()?0:s->parameterNamesRef.data(); + s->name.writeUtf8(d); d += s->name.utf8length(); + *d++ = '('; + + for (int jj = 0; jj < s->parameterNames.count(); ++jj) { + if (jj != 0) { *d++ = ','; *d2++ = ','; } + strcpy(d, s->parameterTypes.at(jj).constData()); + d += s->parameterTypes.at(jj).length(); + s->parameterNames.at(jj).writeUtf8(d2); + d2 += s->parameterNames.at(jj).utf8length(); + } + *d++ = ')'; + *d = 0; + if (d2) *d2 = 0; + } + + // Now allocate methods + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + char *d = s->signatureRef.data(); + char *d2 = s->parameterNamesRef.isEmpty()?0:s->parameterNamesRef.data(); + s->name.writeUtf8(d); d += s->name.utf8length(); + *d++ = '('; + for (int jj = 0; jj < s->parameterNames.count(); ++jj) { + if (jj != 0) { *d++ = ','; *d2++ = ','; } + strcpy(d, "QVariant"); + d += strlen("QVariant"); + strcpy(d2, s->parameterNames.at(jj).constData()); + d2 += s->parameterNames.at(jj).length(); + } + *d++ = ')'; + *d = 0; + if (d2) *d2 = 0; + } + + // Now allocate class name + classNameRef.load(newClassName); + + obj->metadata = builder.toData(); + builder.fromData(&obj->extObject, obj->metatype, obj->metadata); + + if (mode == IgnoreAliases && aliasCount) + compileState->aliasingObjects.append(obj); + + obj->synthdata = dynamicData; + + if (obj->synthCache) { + obj->synthCache->release(); + obj->synthCache = 0; + } + + if (obj->type != -1) { + QQmlPropertyCache *superCache = output->types[obj->type].createPropertyCache(engine); + QQmlPropertyCache *cache = + superCache->copyAndAppend(engine, &obj->extObject, + QQmlPropertyData::NoFlags, + QQmlPropertyData::IsVMEFunction, + QQmlPropertyData::IsVMESignal); + + // now we modify the flags appropriately for var properties. + int propertyOffset = obj->extObject.propertyOffset(); + QQmlPropertyData *currPropData = 0; + for (int pvi = firstPropertyVarIndex; pvi < totalPropCount; ++pvi) { + currPropData = cache->property(pvi + propertyOffset); + currPropData->setFlags(currPropData->getFlags() | QQmlPropertyData::IsVMEProperty); + } + + obj->synthCache = cache; + } + + return true; +} + +bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val) +{ + if (val.isEmpty()) + COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); + + QChar ch = val.at(0); + if (ch.isLetter() && !ch.isLower()) + COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); + + QChar u(QLatin1Char('_')); + if (!ch.isLetter() && ch != u) + COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); + + for (int ii = 1; ii < val.count(); ++ii) { + ch = val.at(ii); + if (!ch.isLetterOrNumber() && ch != u) + COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); + } + + if (enginePrivate->v8engine()->illegalNames().contains(val)) + COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); + + return true; +} + +#include <private/qqmljsparser_p.h> + +static QStringList astNodeToStringList(QQmlJS::AST::Node *node) +{ + if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { + QString name = + static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString(); + return QStringList() << name; + } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { + QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node); + + QStringList rv = astNodeToStringList(expr->base); + if (rv.isEmpty()) + return rv; + rv.append(expr->name.toString()); + return rv; + } + return QStringList(); +} + +bool QQmlCompiler::compileAlias(QFastMetaBuilder &builder, + QByteArray &data, + QQmlScript::Object *obj, + int propIndex, int aliasIndex, + Object::DynamicProperty &prop) +{ + if (!prop.defaultValue) + COMPILE_EXCEPTION(obj, tr("No property alias location")); + + if (!prop.defaultValue->values.isOne() || + prop.defaultValue->values.first()->object || + !prop.defaultValue->values.first()->value.isScript()) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + QQmlJS::AST::Node *node = prop.defaultValue->values.first()->value.asAST(); + if (!node) + COMPILE_EXCEPTION(obj, tr("No property alias location")); // ### Can this happen? + + QStringList alias = astNodeToStringList(node); + + if (alias.count() < 1 || alias.count() > 3) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + + QQmlScript::Object *idObject = compileState->ids.value(alias.at(0)); + if (!idObject) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); + + QByteArray typeName; + + int propIdx = -1; + int flags = 0; + int type = 0; + bool writable = false; + bool resettable = false; + if (alias.count() == 2 || alias.count() == 3) { + propIdx = indexOfProperty(idObject, alias.at(1)); + + if (-1 == propIdx) { + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + } else if (propIdx > 0xFFFF) { + COMPILE_EXCEPTION(prop.defaultValue, tr("Alias property exceeds alias bounds")); + } + + QMetaProperty aliasProperty = idObject->metaObject()->property(propIdx); + if (!aliasProperty.isScriptable()) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + writable = aliasProperty.isWritable() && !prop.isReadOnly; + resettable = aliasProperty.isResettable() && !prop.isReadOnly; + + if (aliasProperty.type() < QVariant::UserType + || uint(aliasProperty.type()) == QMetaType::QVariant) + type = aliasProperty.type(); + + if (alias.count() == 3) { + QQmlValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()]; + if (!valueType) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + propIdx |= ((unsigned int)aliasProperty.type()) << 24; + + int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); + if (valueTypeIndex == -1) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + Q_ASSERT(valueTypeIndex <= 0xFF); + + aliasProperty = valueType->metaObject()->property(valueTypeIndex); + propIdx |= (valueTypeIndex << 16); + + // update the property type + type = aliasProperty.type(); + if (type >= (int)QVariant::UserType) + type = 0; + } + + if (aliasProperty.isEnumType()) + typeName = "int"; // Avoid introducing a dependency on the aliased metaobject + else + typeName = aliasProperty.typeName(); + } else { + Q_ASSERT(idObject->type != -1); // How else did it get an id? + + const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type); + if (ref.type) + typeName = ref.type->typeName(); + else + typeName = ref.component->root->className(); + + typeName += '*'; + } + + if (typeName.endsWith('*')) + flags |= QML_ALIAS_FLAG_PTR; + + QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags }; + + typedef QQmlVMEMetaData VMD; + VMD *vmd = (QQmlVMEMetaData *)data.data(); + *(vmd->aliasData() + aliasIndex) = aliasData; + + prop.nameRef = builder.newString(prop.name.utf8length()); + prop.resolvedCustomTypeName = pool->NewByteArray(typeName); + prop.typeRef = builder.newString(typeName.length()); + + int propertyFlags = 0; + if (writable) + propertyFlags |= QFastMetaBuilder::Writable; + if (resettable) + propertyFlags |= QFastMetaBuilder::Resettable; + + builder.setProperty(propIndex, prop.nameRef, prop.typeRef, (QMetaType::Type)type, + (QFastMetaBuilder::PropertyFlag)propertyFlags, + propIndex); + + return true; +} + +bool QQmlCompiler::buildBinding(QQmlScript::Value *value, + QQmlScript::Property *prop, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(prop->parent); + Q_ASSERT(prop->parent->metaObject()); + + if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration) + COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + + JSBindingReference *reference = pool->New<JSBindingReference>(); + reference->expression = value->value; + reference->property = prop; + reference->value = value; + reference->bindingContext = ctxt; + addBindingReference(reference); + + return true; +} + +bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v, + QQmlScript::Property *prop, + const QQmlCompilerTypes::BindingContext &) +{ + Q_ASSERT(v->value.isScript()); + + if (!prop->core.isWritable()) + return false; + + AST::Node *binding = v->value.asAST(); + + if (prop->type == QVariant::String) { + if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) { + if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) { + if (i->name == qsTrId_string) { + AST::ArgumentList *arg1 = e->arguments?e->arguments:0; + AST::ArgumentList *arg2 = arg1?arg1->next:0; + + if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral && + (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) && + (!arg2 || !arg2->next)) { + + QStringRef text; + int n = -1; + + text = AST::cast<AST::StringLiteral *>(arg1->expression)->value; + if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value; + + TrBindingReference *reference = pool->New<TrBindingReference>(); + reference->dataType = BindingReference::TrId; + reference->text = text; + reference->n = n; + v->bindingReference = reference; + return true; + } + + } else if (i->name == qsTr_string) { + + AST::ArgumentList *arg1 = e->arguments?e->arguments:0; + AST::ArgumentList *arg2 = arg1?arg1->next:0; + AST::ArgumentList *arg3 = arg2?arg2->next:0; + + if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral && + (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) && + (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) && + (!arg3 || !arg3->next)) { + + QStringRef text; + QStringRef comment; + int n = -1; + + text = AST::cast<AST::StringLiteral *>(arg1->expression)->value; + if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value; + if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value; + + TrBindingReference *reference = pool->New<TrBindingReference>(); + reference->dataType = BindingReference::Tr; + reference->text = text; + reference->comment = comment; + reference->n = n; + v->bindingReference = reference; + return true; + } + + } + } + } + + } + + return false; +} + +void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, + QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Property *valueTypeProperty) +{ + Q_UNUSED(obj); + Q_ASSERT(binding->bindingReference); + + const BindingReference &ref = *binding->bindingReference; + if (ref.dataType == BindingReference::TrId) { + const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref); + + Instruction::StoreTrIdString store; + store.propertyIndex = prop->core.coreIndex; + store.text = output->indexForByteArray(tr.text.toUtf8()); + store.n = tr.n; + output->addInstruction(store); + } else if (ref.dataType == BindingReference::Tr) { + const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref); + + Instruction::StoreTrString store; + store.propertyIndex = prop->core.coreIndex; + store.context = translationContextIndex(); + store.text = output->indexForByteArray(tr.text.toUtf8()); + store.comment = output->indexForByteArray(tr.comment.toUtf8()); + store.n = tr.n; + output->addInstruction(store); + } else if (ref.dataType == BindingReference::V4) { + const JSBindingReference &js = static_cast<const JSBindingReference &>(ref); + + Instruction::StoreV4Binding store; + store.value = js.compiledIndex; + store.context = js.bindingContext.stack; + store.owner = js.bindingContext.owner; + if (valueTypeProperty) { + store.property = (valueTypeProperty->index & 0xFFFF) | + ((valueTypeProperty->type & 0xFF)) << 16 | + ((prop->index & 0xFF) << 24); + store.isRoot = (compileState->root == valueTypeProperty->parent); + } else { + store.property = prop->index; + store.isRoot = (compileState->root == obj); + } + store.line = binding->location.start.line; + store.column = binding->location.start.column; + output->addInstruction(store); + } else if (ref.dataType == BindingReference::V8) { + const JSBindingReference &js = static_cast<const JSBindingReference &>(ref); + + Instruction::StoreV8Binding store; + store.value = js.compiledIndex; + store.context = js.bindingContext.stack; + store.owner = js.bindingContext.owner; + if (valueTypeProperty) { + store.isRoot = (compileState->root == valueTypeProperty->parent); + } else { + store.isRoot = (compileState->root == obj); + } + store.line = binding->location.start.line; + store.column = binding->location.start.column; + + Q_ASSERT(js.bindingContext.owner == 0 || + (js.bindingContext.owner != 0 && valueTypeProperty)); + if (js.bindingContext.owner) { + store.property = genValueTypeData(prop, valueTypeProperty); + } else { + store.property = prop->core; + } + + output->addInstruction(store); + } else if (ref.dataType == BindingReference::QtScript) { + const JSBindingReference &js = static_cast<const JSBindingReference &>(ref); + + QQmlInstruction store; + store.assignBinding.value = output->indexForString(js.rewrittenExpression); + store.assignBinding.context = js.bindingContext.stack; + store.assignBinding.owner = js.bindingContext.owner; + store.assignBinding.line = binding->location.start.line; + store.assignBinding.column = binding->location.start.column; + + if (valueTypeProperty) { + store.assignBinding.isRoot = (compileState->root == valueTypeProperty->parent); + } else { + store.assignBinding.isRoot = (compileState->root == obj); + } + + Q_ASSERT(js.bindingContext.owner == 0 || + (js.bindingContext.owner != 0 && valueTypeProperty)); + if (js.bindingContext.owner) { + store.assignBinding.property = genValueTypeData(prop, valueTypeProperty); + } else { + store.assignBinding.property = prop->core; + } + output->addInstructionHelper( + !prop->isAlias ? QQmlInstruction::StoreBinding + : QQmlInstruction::StoreBindingOnAlias + , store); + } else { + Q_ASSERT(!"Unhandled BindingReference::DataType type"); + } +} + +int QQmlCompiler::genContextCache() +{ + if (compileState->ids.count() == 0) + return -1; + + QQmlIntegerCache *cache = new QQmlIntegerCache(); + cache->reserve(compileState->ids.count()); + for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o)) + cache->add(o->id, o->idIndex); + + output->contextCaches.append(cache); + return output->contextCaches.count() - 1; +} + +QQmlPropertyData +QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp, + QQmlScript::Property *prop) +{ + typedef QQmlPropertyPrivate QDPP; + return QDPP::saveValueType(prop->parent->metaObject(), prop->index, + enginePrivate->valueTypes[prop->type]->metaObject(), + valueTypeProp->index, engine); +} + +bool QQmlCompiler::completeComponentBuild() +{ + if (componentStats) + componentStats->componentStat.ids = compileState->ids.count(); + + for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject; + aliasObject = compileState->aliasingObjects.next(aliasObject)) + COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases)); + + QV4Compiler::Expression expr(unit->imports()); + expr.component = compileState->root; + expr.ids = &compileState->ids; + expr.importCache = output->importCache; + + QV4Compiler bindingCompiler; + + QList<JSBindingReference*> sharedBindings; + + for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { + + JSBindingReference &binding = *b; + + // ### We don't currently optimize for bindings on alias's - because + // of the solution to QTBUG-13719 + if (!binding.property->isAlias) { + expr.context = binding.bindingContext.object; + expr.property = binding.property; + expr.expression = binding.expression; + + int index = bindingCompiler.compile(expr, enginePrivate); + if (index != -1) { + binding.dataType = BindingReference::V4; + binding.compiledIndex = index; + if (componentStats) + componentStats->componentStat.optimizedBindings.append(b->value->location); + continue; + } + } + + // Pre-rewrite the expression + QString expression = binding.expression.asScript(); + + QQmlRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString()); + bool isSharable = false; + binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable); + + if (isSharable && !binding.property->isValueTypeSubProperty && !binding.property->isAlias /* See above re alias */ && + binding.property->type != qMetaTypeId<QQmlBinding*>()) { + binding.dataType = BindingReference::V8; + sharedBindings.append(b); + } else { + binding.dataType = BindingReference::QtScript; + } + + if (componentStats) + componentStats->componentStat.scriptBindings.append(b->value->location); + } + + if (!sharedBindings.isEmpty()) { + struct Sort { + static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs) + { + return lhs->value->location.start.line < rhs->value->location.start.line; + } + }; + + qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt); + + int startLineNumber = sharedBindings.at(0)->value->location.start.line; + int lineNumber = startLineNumber; + + QByteArray functionArray("[", 1); + for (int ii = 0; ii < sharedBindings.count(); ++ii) { + + JSBindingReference *reference = sharedBindings.at(ii); + QQmlScript::Value *value = reference->value; + const QString &expression = reference->rewrittenExpression; + + if (ii != 0) functionArray.append(",", 1); + + while (lineNumber < value->location.start.line) { + lineNumber++; + functionArray.append("\n", 1); + } + + functionArray += expression.toUtf8(); + lineNumber += expression.count(QLatin1Char('\n')); + reference->compiledIndex = ii; + } + functionArray.append("]", 1); + + compileState->v8BindingProgram = functionArray; + compileState->v8BindingProgramLine = startLineNumber; + } + + if (bindingCompiler.isValid()) + compileState->compiledBindingData = bindingCompiler.program(); + + // Check pop()'s matched push()'s + Q_ASSERT(compileState->objectDepth.depth() == 0); + Q_ASSERT(compileState->listDepth.depth() == 0); + + saveComponentState(); + + return true; +} + +void QQmlCompiler::dumpStats() +{ + Q_ASSERT(componentStats); + qWarning().nospace() << "QML Document: " << output->url.toString(); + for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) { + const ComponentStat &stat = componentStats->savedComponentStats.at(ii); + qWarning().nospace() << " Component Line " << stat.lineNumber; + qWarning().nospace() << " Total Objects: " << stat.objects; + qWarning().nospace() << " IDs Used: " << stat.ids; + qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count(); + + { + QByteArray output; + for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) { + if (0 == (ii % 10)) { + if (ii) output.append("\n"); + output.append(" "); + } + + output.append("("); + output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line)); + output.append(":"); + output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column)); + output.append(") "); + } + if (!output.isEmpty()) + qWarning().nospace() << output.constData(); + } + + qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count(); + { + QByteArray output; + for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) { + if (0 == (ii % 10)) { + if (ii) output.append("\n"); + output.append(" "); + } + + output.append("("); + output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line)); + output.append(":"); + output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column)); + output.append(") "); + } + if (!output.isEmpty()) + qWarning().nospace() << output.constData(); + } + } +} + +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from) +{ + const QMetaObject *toMo = enginePrivate->rawMetaObjectForType(to); + const QMetaObject *fromMo = from->metaObject(); + + while (fromMo) { + if (QQmlPropertyPrivate::equal(fromMo, toMo)) + return true; + fromMo = fromMo->superClass(); + } + return false; +} + +/*! + Returns the element name, as written in the QML file, for o. +*/ +QString QQmlCompiler::elementName(QQmlScript::Object *o) +{ + Q_ASSERT(o); + if (o->type != -1) { + return output->types.at(o->type).className; + } else { + return QString(); + } +} + +QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from) +{ + // ### Optimize + const QMetaObject *mo = from->metatype; + QQmlType *type = 0; + while (!type && mo) { + type = QQmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + return type; +} + +QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj) +{ + const QMetaObject *mo = obj->metatype; + + int idx = mo->indexOfClassInfo("DeferredPropertyNames"); + if (idx == -1) + return QStringList(); + + QMetaClassInfo classInfo = mo->classInfo(idx); + QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + return rv; +} + +QQmlPropertyData * +QQmlCompiler::property(QQmlScript::Object *object, int index) +{ + QQmlPropertyCache *cache = 0; + + if (object->synthCache) + cache = object->synthCache; + else if (object->type != -1) + cache = output->types[object->type].createPropertyCache(engine); + else + cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + + return cache->property(index); +} + +QQmlPropertyData * +QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyCache *cache = 0; + + if (object->synthCache) + cache = object->synthCache; + else if (object->type != -1) + cache = output->types[object->type].createPropertyCache(engine); + else + cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + + QQmlPropertyData *d = cache->property(name); + + // Find the first property + while (d && d->isFunction()) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return 0; + } else { + return d; + } +} + +// This code must match the semantics of QQmlPropertyPrivate::findSignalByName +QQmlPropertyData * +QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyCache *cache = 0; + + if (object->synthCache) + cache = object->synthCache; + else if (object->type != -1) + cache = output->types[object->type].createPropertyCache(engine); + else + cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + + + QQmlPropertyData *d = cache->property(name); + if (notInRevision) *notInRevision = false; + + while (d && !(d->isFunction())) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return 0; + } else if (d) { + return d; + } + + if (name.endsWith(Changed_string)) { + QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length()); + + d = property(object, propName, notInRevision); + if (d) + return cache->method(d->notifyIndex); + } + + return 0; +} + +// This code must match the semantics of QQmlPropertyPrivate::findSignalByName +int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name, + bool *notInRevision) +{ + QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision); + return d?d->coreIndex:-1; +} + +int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name, + bool *notInRevision) +{ + return indexOfProperty(object, QStringRef(&name), notInRevision); +} + +int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name, + bool *notInRevision) +{ + QQmlPropertyData *d = property(object, name, notInRevision); + return d?d->coreIndex:-1; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h new file mode 100644 index 0000000000..9b13b7e63b --- /dev/null +++ b/src/qml/qml/qqmlcompiler_p.h @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCOMPILER_P_H +#define QQMLCOMPILER_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 "qqml.h" +#include "qqmlerror.h" +#include <private/qv8_p.h> +#include "qqmlinstruction_p.h" +#include "qqmlscript_p.h" +#include "qqmlengine_p.h" +#include <private/qbitfield_p.h> +#include "qqmlpropertycache_p.h" +#include "qqmlintegercache_p.h" +#include "qqmltypenamecache_p.h" +#include "qqmltypeloader_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qset.h> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class QQmlComponent; +class QQmlContext; +class QQmlContextData; + +class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, + public QQmlCleanup +{ +public: + QQmlCompiledData(QQmlEngine *engine); + virtual ~QQmlCompiledData(); + + QQmlEngine *engine; + + QString name; + QUrl url; + QQmlTypeNameCache *importCache; + + struct TypeReference + { + TypeReference() + : type(0), typePropertyCache(0), component(0) {} + + QString className; + QQmlType *type; + QQmlPropertyCache *typePropertyCache; + QQmlCompiledData *component; + + const QMetaObject *metaObject() const; + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + }; + QList<TypeReference> types; + + struct V8Program { + V8Program(const QByteArray &p, QQmlCompiledData *c) + : program(p), cdata(c) {} + + QByteArray program; + v8::Persistent<v8::Array> bindings; + QQmlCompiledData *cdata; + }; + + QList<V8Program> programs; + + const QMetaObject *root; + QAbstractDynamicMetaObject rootData; + QQmlPropertyCache *rootPropertyCache; + QList<QString> primitives; + QList<QByteArray> datas; + QByteArray bytecode; + QList<QQmlPropertyCache *> propertyCaches; + QList<QQmlIntegerCache *> contextCaches; + QList<QQmlScriptData *> scripts; + QList<QUrl> urls; + + struct Instruction { +#define QML_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlInstructionData<QQmlInstruction::I> I; + FOR_EACH_QML_INSTR(QML_INSTR_DATA_TYPEDEF) +#undef QML_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + void dumpInstructions(); + + template <int Instr> + int addInstruction(const QQmlInstructionData<Instr> &data) + { + QQmlInstruction genericInstr; + QQmlInstructionMeta<Instr>::setData(genericInstr, data); + return addInstructionHelper(static_cast<QQmlInstruction::Type>(Instr), genericInstr); + } + int nextInstructionIndex(); + QQmlInstruction *instruction(int index); + QQmlInstruction::Type instructionType(const QQmlInstruction *instr); + + bool isInitialized() const { return hasEngine(); } + void initialize(QQmlEngine *); + +protected: + virtual void destroy(); // From QQmlRefCount + virtual void clear(); // From QQmlCleanup + +private: + friend class QQmlCompiler; + + int addInstructionHelper(QQmlInstruction::Type type, QQmlInstruction &instr); + void dump(QQmlInstruction *, int idx = -1); + QQmlCompiledData(const QQmlCompiledData &other); + QQmlCompiledData &operator=(const QQmlCompiledData &other); + QByteArray packData; + int pack(const char *, size_t); + + int indexForString(const QString &); + int indexForByteArray(const QByteArray &); + int indexForUrl(const QUrl &); +}; + +namespace QQmlCompilerTypes { + struct BindingContext + { + BindingContext() + : stack(0), owner(0), object(0) {} + BindingContext(QQmlScript::Object *o) + : stack(0), owner(0), object(o) {} + BindingContext incr() const { + BindingContext rv(object); + rv.stack = stack + 1; + return rv; + } + bool isSubContext() const { return stack != 0; } + int stack; + int owner; + QQmlScript::Object *object; + }; + + struct BindingReference + { + enum DataType { QtScript, V4, V8, + Tr, TrId }; + DataType dataType; + }; + + struct JSBindingReference : public QQmlPool::Class, + public BindingReference + { + JSBindingReference() : nextReference(0) {} + + QQmlScript::Variant expression; + QQmlScript::Property *property; + QQmlScript::Value *value; + + int compiledIndex; + + QString rewrittenExpression; + BindingContext bindingContext; + + JSBindingReference *nextReference; + }; + + struct TrBindingReference : public QQmlPool::POD, + public BindingReference + { + QStringRef text; + QStringRef comment; + int n; + }; + + struct IdList : public QFieldList<QQmlScript::Object, + &QQmlScript::Object::nextIdObject> + { + QQmlScript::Object *value(const QString &id) const { + for (QQmlScript::Object *o = first(); o; o = next(o)) { + if (o->id == id) + return o; + } + return 0; + } + }; + + struct DepthStack { + DepthStack() : _depth(0), _maxDepth(0) {} + DepthStack(const DepthStack &o) : _depth(o._depth), _maxDepth(o._maxDepth) {} + DepthStack &operator=(const DepthStack &o) { _depth = o._depth; _maxDepth = o._maxDepth; return *this; } + + int depth() const { return _depth; } + int maxDepth() const { return _maxDepth; } + + void push() { ++_depth; _maxDepth = qMax(_depth, _maxDepth); } + void pop() { --_depth; Q_ASSERT(_depth >= 0); Q_ASSERT(_maxDepth > _depth); } + + void pushPop(int count) { _maxDepth = qMax(_depth + count, _maxDepth); } + private: + int _depth; + int _maxDepth; + }; + + // Contains all the incremental compiler state about a component. As + // a single QML file can have multiple components defined, there may be + // more than one of these for each compile + struct ComponentCompileState : public QQmlPool::Class + { + ComponentCompileState() + : parserStatusCount(0), totalBindingsCount(0), pushedProperties(0), nested(false), + v8BindingProgramLine(-1), root(0) {} + + IdList ids; + int parserStatusCount; + int totalBindingsCount; + int pushedProperties; + bool nested; + + QByteArray compiledBindingData; + QByteArray v8BindingProgram; + int v8BindingProgramLine; + + DepthStack objectDepth; + DepthStack listDepth; + + typedef QQmlCompilerTypes::JSBindingReference B; + typedef QFieldList<B, &B::nextReference> JSBindingReferenceList; + JSBindingReferenceList bindings; + typedef QQmlScript::Object O; + typedef QFieldList<O, &O::nextAliasingObject> AliasingObjectsList; + AliasingObjectsList aliasingObjects; + QQmlScript::Object *root; + }; +}; + +class QMetaObjectBuilder; +class Q_AUTOTEST_EXPORT QQmlCompiler +{ + Q_DECLARE_TR_FUNCTIONS(QQmlCompiler) +public: + QQmlCompiler(QQmlPool *); + + bool compile(QQmlEngine *, QQmlTypeData *, QQmlCompiledData *); + + bool isError() const; + QList<QQmlError> errors() const; + + static bool isAttachedPropertyName(const QString &); + static bool isSignalPropertyName(const QString &); + static bool isAttachedPropertyName(const QHashedStringRef &); + static bool isSignalPropertyName(const QHashedStringRef &); + + int evaluateEnum(const QByteArray& script) const; // for QQmlCustomParser::evaluateEnum + const QMetaObject *resolveType(const QString& name) const; // for QQmlCustomParser::resolveType + int rewriteBinding(const QQmlScript::Variant& value, const QString& name); // for QQmlCustomParser::rewriteBinding + QString rewriteSignalHandler(const QQmlScript::Variant& value, const QString &name); // for QQmlCustomParser::rewriteSignalHandler + +private: + typedef QQmlCompiledData::Instruction Instruction; + + static void reset(QQmlCompiledData *); + + void compileTree(QQmlScript::Object *tree); + + + bool buildObject(QQmlScript::Object *obj, const QQmlCompilerTypes::BindingContext &); + bool buildComponent(QQmlScript::Object *obj, const QQmlCompilerTypes::BindingContext &); + bool buildSubObject(QQmlScript::Object *obj, const QQmlCompilerTypes::BindingContext &); + bool buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &); + bool buildProperty(QQmlScript::Property *prop, QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &); + bool buildPropertyInNamespace(QQmlImportedNamespace *ns, + QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &); + bool buildIdProperty(QQmlScript::Property *prop, QQmlScript::Object *obj); + bool buildAttachedProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildGroupedProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildValueTypeProperty(QObject *type, + QQmlScript::Object *obj, + QQmlScript::Object *baseObj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildListProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildScriptStringProperty(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildPropertyAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildPropertyObjectAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *value, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildPropertyOnAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Object *baseObj, + QQmlScript::Value *value, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildPropertyLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *value, + const QQmlCompilerTypes::BindingContext &ctxt); + bool doesPropertyExist(QQmlScript::Property *prop, QQmlScript::Object *obj); + bool testLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Value *value); + bool testQualifiedEnumAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Value *value, + bool *isAssignment); + enum DynamicMetaMode { IgnoreAliases, ResolveAliases, ForceCreation }; + bool mergeDynamicMetaProperties(QQmlScript::Object *obj); + bool buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode); + bool checkDynamicMeta(QQmlScript::Object *obj); + bool buildBinding(QQmlScript::Value *, QQmlScript::Property *prop, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildLiteralBinding(QQmlScript::Value *, QQmlScript::Property *prop, + const QQmlCompilerTypes::BindingContext &ctxt); + bool buildComponentFromRoot(QQmlScript::Object *obj, const QQmlCompilerTypes::BindingContext &); + bool compileAlias(QFastMetaBuilder &, + QByteArray &data, + QQmlScript::Object *obj, + int propIndex, int aliasIndex, + QQmlScript::Object::DynamicProperty &); + bool completeComponentBuild(); + bool checkValidId(QQmlScript::Value *, const QString &); + + + void genObject(QQmlScript::Object *obj); + void genObjectBody(QQmlScript::Object *obj); + void genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *); + void genComponent(QQmlScript::Object *obj); + void genValueProperty(QQmlScript::Property *prop, QQmlScript::Object *obj); + void genListProperty(QQmlScript::Property *prop, QQmlScript::Object *obj); + void genPropertyAssignment(QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Property *valueTypeProperty = 0); + void genLiteralAssignment(QQmlScript::Property *prop, + QQmlScript::Value *value); + void genBindingAssignment(QQmlScript::Value *binding, + QQmlScript::Property *prop, + QQmlScript::Object *obj, + QQmlScript::Property *valueTypeProperty = 0); + int genContextCache(); + + QQmlPropertyData genValueTypeData(QQmlScript::Property *prop, + QQmlScript::Property *valueTypeProp); + + int componentTypeRef(); + int translationContextIndex(); + + static QQmlType *toQmlType(QQmlScript::Object *from); + bool canCoerce(int to, QQmlScript::Object *from); + + QString elementName(QQmlScript::Object *); + + QStringList deferredProperties(QQmlScript::Object *); + + QQmlPropertyData *property(QQmlScript::Object *, int); + QQmlPropertyData *property(QQmlScript::Object *, const QHashedStringRef &, + bool *notInRevision = 0); + QQmlPropertyData *signal(QQmlScript::Object *, const QHashedStringRef &, + bool *notInRevision = 0); + int indexOfProperty(QQmlScript::Object *, const QHashedStringRef &, bool *notInRevision = 0); + int indexOfProperty(QQmlScript::Object *, const QString &, bool *notInRevision = 0); + int indexOfSignal(QQmlScript::Object *, const QString &, bool *notInRevision = 0); + + void addId(const QString &, QQmlScript::Object *); + + void dumpStats(); + + void addBindingReference(QQmlCompilerTypes::JSBindingReference *); + + QQmlCompilerTypes::ComponentCompileState *compileState; + + QQmlPool *pool; + + QQmlCompilerTypes::ComponentCompileState *componentState(QQmlScript::Object *); + void saveComponentState(); + + QList<QQmlError> exceptions; + QQmlCompiledData *output; + QQmlEngine *engine; + QQmlEnginePrivate *enginePrivate; + QQmlScript::Object *unitRoot; + QQmlTypeData *unit; + int cachedComponentTypeRef; + int cachedTranslationContextIndex; + + // Compiler component statistics. Only collected if QML_COMPILER_STATS=1 + struct ComponentStat + { + ComponentStat() : ids(0), objects(0) {} + + int lineNumber; + + int ids; + QList<QQmlScript::LocationSpan> scriptBindings; + QList<QQmlScript::LocationSpan> optimizedBindings; + int objects; + }; + struct ComponentStats : public QQmlPool::Class + { + ComponentStat componentStat; + QList<ComponentStat> savedComponentStats; + }; + ComponentStats *componentStats; +}; + +QT_END_NAMESPACE + +#endif // QQMLCOMPILER_P_H diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp new file mode 100644 index 0000000000..e168f063c0 --- /dev/null +++ b/src/qml/qml/qqmlcomponent.cpp @@ -0,0 +1,1350 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcomponent.h" +#include "qqmlcomponent_p.h" +#include "qqmlcomponentattached_p.h" + +#include "qqmlcompiler_p.h" +#include "qqmlcontext_p.h" +#include "qqmlengine_p.h" +#include "qqmlvme_p.h" +#include "qqml.h" +#include "qqmlengine.h" +#include "qqmlbinding_p.h" +#include "qqmlbinding_p_p.h" +#include "qqmlscript_p.h" +#include <private/qqmlprofilerservice_p.h> +#include <private/qqmlenginedebugservice_p.h> +#include "qqmlincubator.h" +#include "qqmlincubator_p.h" + +#include <private/qv8engine_p.h> +#include <private/qv8include_p.h> + +#include <QStack> +#include <QStringList> +#include <QtCore/qdebug.h> +#include <qqmlinfo.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponentExtension : public QV8Engine::Deletable +{ +public: + QQmlComponentExtension(QV8Engine *); + virtual ~QQmlComponentExtension(); + + v8::Persistent<v8::Function> incubationConstructor; + v8::Persistent<v8::Script> initialProperties; + v8::Persistent<v8::Function> forceCompletion; +}; +V8_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension); + +/* + Try to do what's necessary for a reasonable display of the type + name, but no more (just enough for the client to do more extensive cleanup). + + Should only be called when debugging is enabled. +*/ +static inline QString buildTypeNameForDebug(const QMetaObject *metaObject) +{ + static const QString qmlMarker(QLatin1String("_QML")); + static const QChar underscore(QLatin1Char('_')); + static const QChar asterisk(QLatin1Char('*')); + QQmlType *type = QQmlMetaType::qmlType(metaObject); + QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className()); + if (!type) { + //### optimize further? + int marker = typeName.indexOf(qmlMarker); + if (marker != -1 && marker < typeName.count() - 1) { + if (typeName[marker + 1] == underscore) { + const QString className = typeName.left(marker) + asterisk; + type = QQmlMetaType::qmlType(QMetaType::type(className.toUtf8())); + if (type) + typeName = type->qmlTypeName(); + } + } + } + return typeName; +} + +/*! + \class QQmlComponent + \since 4.7 + \brief The QQmlComponent class encapsulates a QML component definition. + \mainclass + + Components are reusable, encapsulated QML elements with well-defined interfaces. + They are often defined in \l {qdeclarativedocuments.html}{Component Files}. + + A QQmlComponent instance can be created from a QML file. + For example, if there is a \c main.qml file like this: + + \qml + import QtQuick 2.0 + + Item { + width: 200 + height: 200 + } + \endqml + + The following code loads this QML file as a component, creates an instance of + this component using create(), and then queries the \l Item's \l {Item::}{width} + value: + + \code + QQmlEngine *engine = new QQmlEngine; + QQmlComponent component(engine, QUrl::fromLocalFile("main.qml")); + + QObject *myObject = component.create(); + QQuickItem *item = qobject_cast<QQuickItem*>(myObject); + int width = item->width(); // width = 200 + \endcode + + + \section2 Network Components + + If the URL passed to QQmlComponent is a network resource, or if the QML document references a + network resource, the QQmlComponent has to fetch the network data before it is able to create + objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading} + \l {QQmlComponent::status()}{status}. An application will have to wait until the component + is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}. + + The following example shows how to load a QML file from a network resource. After creating + the QQmlComponent, it tests whether the component is loading. If it is, it connects to the + QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method + directly. Note that QQmlComponent::isLoading() may be false for a network component if the + component has been cached and is ready immediately. + + \code + MyApplication::MyApplication() + { + // ... + component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml")); + if (component->isLoading()) + QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)), + this, SLOT(continueLoading())); + else + continueLoading(); + } + + void MyApplication::continueLoading() + { + if (component->isError()) { + qWarning() << component->errors(); + } else { + QObject *myObject = component->create(); + } + } + \endcode + + \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code} +*/ + +/*! + \qmlclass Component QQmlComponent + \ingroup qml-utility-elements + \since 4.7 + \brief The Component element encapsulates a QML component definition. + + Components are reusable, encapsulated QML elements with well-defined interfaces. + + Components are often defined by \l {qdeclarativedocuments.html}{component files} - + that is, \c .qml files. The \e Component element essentially allows QML components + to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file. + This may be useful for reusing a small component within a QML file, or for defining + a component that logically belongs with other QML components within a file. + + For example, here is a component that is used by multiple \l Loader objects. + It contains a single item, a \l Rectangle: + + \snippet doc/src/snippets/qml/component.qml 0 + + Notice that while a \l Rectangle by itself would be automatically + rendered and displayed, this is not the case for the above rectangle + because it is defined inside a \c Component. The component encapsulates the + QML elements within, as if they were defined in a separate QML + file, and is not loaded until requested (in this case, by the + two \l Loader objects). + + Defining a \c Component is similar to defining a \l {QML Document}{QML document}. + A QML document has a single top-level item that defines the behaviors and + properties of that component, and cannot define properties or behaviors outside + of that top-level item. In the same way, a \c Component definition contains a single + top level item (which in the above example is a \l Rectangle) and cannot define any + data outside of this item, with the exception of an \e id (which in the above example + is \e redSquare). + + The \c Component element is commonly used to provide graphical components + for views. For example, the ListView::delegate property requires a \c Component + to specify how each list item is to be displayed. + + \c Component objects can also be created dynamically using + \l{QML:Qt::createComponent()}{Qt.createComponent()}. +*/ + +/*! + \qmlattachedsignal Component::onCompleted() + + Emitted after component "startup" has completed. This can be used to + execute script code at startup, once the full QML environment has been + established. + + The \c {Component::onCompleted} attached property can be applied to + any element. The order of running the \c onCompleted scripts is + undefined. + + \qml + Rectangle { + Component.onCompleted: console.log("Completed Running!") + Rectangle { + Component.onCompleted: console.log("Nested Completed Running!") + } + } + \endqml +*/ + +/*! + \qmlattachedsignal Component::onDestruction() + + Emitted as the component begins destruction. This can be used to undo + work done in the onCompleted signal, or other imperative code in your + application. + + The \c {Component::onDestruction} attached property can be applied to + any element. However, it applies to the destruction of the component as + a whole, and not the destruction of the specific object. The order of + running the \c onDestruction scripts is undefined. + + \qml + Rectangle { + Component.onDestruction: console.log("Destruction Beginning!") + Rectangle { + Component.onDestruction: console.log("Nested Destruction Beginning!") + } + } + \endqml + + \sa QtQml +*/ + +/*! + \enum QQmlComponent::Status + + Specifies the loading status of the QQmlComponent. + + \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content. + \value Ready This QQmlComponent is ready and create() may be called. + \value Loading This QQmlComponent is loading network data. + \value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}. +*/ + +void QQmlComponentPrivate::typeDataReady(QQmlTypeData *) +{ + Q_Q(QQmlComponent); + + Q_ASSERT(typeData); + + fromTypeData(typeData); + typeData = 0; + + emit q->statusChanged(q->status()); +} + +void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p) +{ + Q_Q(QQmlComponent); + + progress = p; + + emit q->progressChanged(p); +} + +void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) +{ + url = data->finalUrl(); + QQmlCompiledData *c = data->compiledData(); + + if (!c) { + Q_ASSERT(data->isError()); + state.errors = data->errors(); + } else { + cc = c; + } + + data->release(); +} + +void QQmlComponentPrivate::clear() +{ + if (typeData) { + typeData->unregisterCallback(this); + typeData->release(); + typeData = 0; + } + + if (cc) { + cc->release(); + cc = 0; + } +} + +/*! + \internal +*/ +QQmlComponent::QQmlComponent(QObject *parent) + : QObject(*(new QQmlComponentPrivate), parent) +{ +} + +/*! + Destruct the QQmlComponent. +*/ +QQmlComponent::~QQmlComponent() +{ + Q_D(QQmlComponent); + + if (d->state.completePending) { + qWarning("QQmlComponent: Component destroyed while completion pending"); + d->completeCreate(); + } + + if (d->typeData) { + d->typeData->unregisterCallback(d); + d->typeData->release(); + } + if (d->cc) + d->cc->release(); +} + +/*! + \qmlproperty enumeration Component::status + This property holds the status of component loading. It can be one of: + \list + \o Component.Null - no data is available for the component + \o Component.Ready - the component has been loaded, and can be used to create instances. + \o Component.Loading - the component is currently being loaded + \o Component.Error - an error occurred while loading the component. + Calling errorString() will provide a human-readable description of any errors. + \endlist + */ + +/*! + \property QQmlComponent::status + The component's current \l{QQmlComponent::Status} {status}. + */ +QQmlComponent::Status QQmlComponent::status() const +{ + Q_D(const QQmlComponent); + + if (d->typeData) + return Loading; + else if (!d->state.errors.isEmpty()) + return Error; + else if (d->engine && d->cc) + return Ready; + else + return Null; +} + +/*! + Returns true if status() == QQmlComponent::Null. +*/ +bool QQmlComponent::isNull() const +{ + return status() == Null; +} + +/*! + Returns true if status() == QQmlComponent::Ready. +*/ +bool QQmlComponent::isReady() const +{ + return status() == Ready; +} + +/*! + Returns true if status() == QQmlComponent::Error. +*/ +bool QQmlComponent::isError() const +{ + return status() == Error; +} + +/*! + Returns true if status() == QQmlComponent::Loading. +*/ +bool QQmlComponent::isLoading() const +{ + return status() == Loading; +} + +/*! + \qmlproperty real Component::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ + +/*! + \property QQmlComponent::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ +qreal QQmlComponent::progress() const +{ + Q_D(const QQmlComponent); + return d->progress; +} + +/*! + \fn void QQmlComponent::progressChanged(qreal progress) + + Emitted whenever the component's loading progress changes. \a progress will be the + current progress between 0.0 (nothing loaded) and 1.0 (finished). +*/ + +/*! + \fn void QQmlComponent::statusChanged(QQmlComponent::Status status) + + Emitted whenever the component's status changes. \a status will be the + new status. +*/ + +/*! + Create a QQmlComponent with no data and give it the specified + \a engine and \a parent. Set the data with setData(). +*/ +QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) + : QObject(*(new QQmlComponentPrivate), parent) +{ + Q_D(QQmlComponent); + d->engine = engine; +} + +/*! + Create a QQmlComponent from the given \a url and give it the + specified \a parent and \a engine. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + \sa loadUrl() +*/ +QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent) +: QObject(*(new QQmlComponentPrivate), parent) +{ + Q_D(QQmlComponent); + d->engine = engine; + loadUrl(url); +} + +/*! + Create a QQmlComponent from the given \a fileName and give it the specified + \a parent and \a engine. + + \sa loadUrl() +*/ +QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, + QObject *parent) +: QObject(*(new QQmlComponentPrivate), parent) +{ + Q_D(QQmlComponent); + d->engine = engine; + loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); +} + +/*! + \internal +*/ +QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent) + : QObject(*(new QQmlComponentPrivate), parent) +{ + Q_D(QQmlComponent); + d->engine = engine; + d->cc = cc; + cc->addref(); + d->start = start; + d->url = cc->url; + d->progress = 1.0; +} + +/*! + Sets the QQmlComponent to use the given QML \a data. If \a url + is provided, it is used to set the component name and to provide + a base path for items resolved by this component. +*/ +void QQmlComponent::setData(const QByteArray &data, const QUrl &url) +{ + Q_D(QQmlComponent); + + d->clear(); + + d->url = url; + + QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.get(data, url); + + if (typeData->isCompleteOrError()) { + d->fromTypeData(typeData); + } else { + d->typeData = typeData; + d->typeData->registerCallback(d); + } + + d->progress = 1.0; + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! +Returns the QQmlContext the component was created in. This is only +valid for components created directly from QML. +*/ +QQmlContext *QQmlComponent::creationContext() const +{ + Q_D(const QQmlComponent); + if(d->creationContext) + return d->creationContext->asQQmlContext(); + + return qmlContext(this); +} + +/*! + Load the QQmlComponent from the provided \a url. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. +*/ +void QQmlComponent::loadUrl(const QUrl &url) +{ + Q_D(QQmlComponent); + + d->clear(); + + if ((url.isRelative() && !url.isEmpty()) + || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929 + d->url = d->engine->baseUrl().resolved(url); + else + d->url = url; + + if (url.isEmpty()) { + QQmlError error; + error.setDescription(tr("Invalid empty URL")); + d->state.errors << error; + return; + } + + QQmlTypeData *data = QQmlEnginePrivate::get(d->engine)->typeLoader.get(d->url); + + if (data->isCompleteOrError()) { + d->fromTypeData(data); + d->progress = 1.0; + } else { + d->typeData = data; + d->typeData->registerCallback(d); + d->progress = data->progress(); + } + + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. An empty list is returned if isError() is not set. +*/ +QList<QQmlError> QQmlComponent::errors() const +{ + Q_D(const QQmlComponent); + if (isError()) + return d->state.errors; + else + return QList<QQmlError>(); +} + +/*! + \qmlmethod string Component::errorString() + + Returns a human-readable description of any errors. + + The string includes the file, location, and description of each error. + If multiple errors are present they are separated by a newline character. + + If no errors are present, an empty string is returned. +*/ + +/*! + \internal + errorString is only meant as a way to get the errors in script +*/ +QString QQmlComponent::errorString() const +{ + Q_D(const QQmlComponent); + QString ret; + if(!isError()) + return ret; + foreach(const QQmlError &e, d->state.errors) { + ret += e.url().toString() + QLatin1Char(':') + + QString::number(e.line()) + QLatin1Char(' ') + + e.description() + QLatin1Char('\n'); + } + return ret; +} + +/*! + \qmlproperty url Component::url + The component URL. This is the URL that was used to construct the component. +*/ + +/*! + \property QQmlComponent::url + The component URL. This is the URL passed to either the constructor, + or the loadUrl() or setData() methods. +*/ +QUrl QQmlComponent::url() const +{ + Q_D(const QQmlComponent); + return d->url; +} + +/*! + \internal +*/ +QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + If \a context is 0 (the default), it will create the instance in the + engine' s \l {QQmlEngine::rootContext()}{root context}. +*/ +QObject *QQmlComponent::create(QQmlContext *context) +{ + Q_D(QQmlComponent); + + if (!context) + context = d->engine->rootContext(); + + QObject *rv = beginCreate(context); + completeCreate(); + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QQmlComponent::create() to create a + component. + + Create an object instance from this component. Returns 0 if creation + failed. \a publicContext specifies the context within which to create the object + instance. + + When QQmlComponent constructs an instance, it occurs in three steps: + \list 1 + \i The object hierarchy is created, and constant values are assigned. + \i Property bindings are evaluated for the the first time. + \i If applicable, QQmlParserStatus::componentComplete() is called on objects. + \endlist + QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it + only performs step 1. QQmlComponent::completeCreate() must be called to + complete steps 2 and 3. + + This breaking point is sometimes useful when using attached properties to + communicate information to an instantiated component, as it allows their + initial values to be configured before property bindings take effect. +*/ +QObject *QQmlComponent::beginCreate(QQmlContext *publicContext) +{ + Q_D(QQmlComponent); + + Q_ASSERT(publicContext); + QQmlContextData *context = QQmlContextData::get(publicContext); + + return d->beginCreate(context); +} + +QObject * +QQmlComponentPrivate::beginCreate(QQmlContextData *context) +{ + Q_Q(QQmlComponent); + if (!context) { + qWarning("QQmlComponent: Cannot create a component in a null context"); + return 0; + } + + if (!context->isValid()) { + qWarning("QQmlComponent: Cannot create a component in an invalid context"); + return 0; + } + + if (context->engine != engine) { + qWarning("QQmlComponent: Must create component in context from the same QQmlEngine"); + return 0; + } + + if (state.completePending) { + qWarning("QQmlComponent: Cannot create new component instance before completing the previous"); + return 0; + } + + if (!q->isReady()) { + qWarning("QQmlComponent: Component is not ready"); + return 0; + } + + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + + bool isRoot = enginePriv->inProgressCreations == 0; + enginePriv->inProgressCreations++; + state.errors.clear(); + state.completePending = true; + + if (isRoot) + QQmlProfilerService::startRange(QQmlProfilerService::Creating); + + enginePriv->referenceScarceResources(); + state.vme.init(context, cc, start, creationContext); + QObject *rv = state.vme.execute(&state.errors); + enginePriv->dereferenceScarceResources(); + + if (rv) { + QQmlData *ddata = QQmlData::get(rv); + Q_ASSERT(ddata); + ddata->indestructible = true; + } + + if (enginePriv->isDebugging && rv) { + if (!context->isInternal) + context->asQQmlContextPrivate()->instances.append(rv); + QQmlEngineDebugService::instance()->objectCreated(engine, rv); + if (isRoot) { + QQmlProfilerService::rangeData(QQmlProfilerService::Creating, + buildTypeNameForDebug(rv->metaObject())); + QQmlData *data = QQmlData::get(rv); + Q_ASSERT(data); + QQmlProfilerService::rangeLocation(QQmlProfilerService::Creating, + cc->url, data->lineNumber, data->columnNumber); + } + } + + return rv; +} + +void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, + QObject *object, ConstructionState *state) +{ + enginePriv->inProgressCreations++; + state->errors.clear(); + state->completePending = true; + + state->vme.initDeferred(object); + state->vme.execute(&state->errors); +} + +void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state) +{ + if (state->completePending) { + state->vme.complete(); + + state->completePending = false; + + enginePriv->inProgressCreations--; + + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QQmlComponent::create() to create a + component. + + Complete a component creation begin with QQmlComponent::beginCreate(). +*/ +void QQmlComponent::completeCreate() +{ + Q_D(QQmlComponent); + + d->completeCreate(); +} + +void QQmlComponentPrivate::completeCreate() +{ + if (state.completePending) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + complete(ep, &state); + + QQmlProfilerService::endRange(QQmlProfilerService::Creating); + } +} + +QQmlComponentAttached::QQmlComponentAttached(QObject *parent) +: QObject(parent), prev(0), next(0) +{ +} + +QQmlComponentAttached::~QQmlComponentAttached() +{ + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} + +/*! + \internal +*/ +QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) +{ + QQmlComponentAttached *a = new QQmlComponentAttached(obj); + + QQmlEngine *engine = qmlEngine(obj); + if (!engine) + return a; + + if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin + QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine); + a->add(&p->activeVME->componentAttached); + } else { + QQmlData *d = QQmlData::get(obj); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + } + + return a; +} + +void QQmlComponent::create(QQmlIncubator &i, QQmlContext *context, + QQmlContext *forContext) +{ + Q_D(QQmlComponent); + + if (!context) + context = d->engine->rootContext(); + + QQmlContextData *contextData = QQmlContextData::get(context); + QQmlContextData *forContextData = contextData; + if (forContext) forContextData = QQmlContextData::get(forContext); + + if (!contextData->isValid()) { + qWarning("QQmlComponent: Cannot create a component in an invalid context"); + return; + } + + if (contextData->engine != d->engine) { + qWarning("QQmlComponent: Must create component in context from the same QQmlEngine"); + return; + } + + if (!isReady()) { + qWarning("QQmlComponent: Component is not ready"); + return; + } + + i.clear(); + QQmlIncubatorPrivate *p = i.d; + + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine); + + p->component = d->cc; p->component->addref(); + p->vme.init(contextData, d->cc, d->start, d->creationContext); + + enginePriv->incubate(i, forContextData); +} + +class QV8IncubatorResource : public QV8ObjectResource, + public QQmlIncubator +{ +V8_RESOURCE_TYPE(IncubatorType) +public: + QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous); + + static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info); + static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info); + static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info); + static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info); + static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args); + + static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, + const v8::AccessorInfo& info); + + void dispose(); + + v8::Persistent<v8::Object> me; + QQmlGuard<QObject> parent; + v8::Persistent<v8::Value> valuemap; + v8::Persistent<v8::Object> qmlGlobal; +protected: + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); +}; + +static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) +{ + if (parent) { + me->setParent(parent); + typedef QQmlPrivate::AutoParentFunction APF; + QList<APF> functions = QQmlMetaType::parentFunctions(); + + bool needParent = false; + for (int ii = 0; ii < functions.count(); ++ii) { + QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent); + if (res == QQmlPrivate::Parented) { + needParent = false; + break; + } else if (res == QQmlPrivate::IncompatibleParent) { + needParent = true; + } + } + if (needParent) + qWarning("QQmlComponent: Created graphical object was not " + "placed in the graphics scene."); + } +} + +/*! + \qmlmethod object Component::createObject(Item parent, object properties) + + Creates and returns an object instance of this component that will have + the given \a parent and \a properties. The \a properties argument is optional. + Returns null if object creation fails. + + The object will be created in the same context as the one in which the component + was created. This function will always return null when called on components + which were not created in QML. + + If you wish to create an object without setting a parent, specify \c null for + the \a parent value. Note that if the returned object is to be displayed, you + must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} + property, or else the object will not be visible. + + If a \a parent is not provided to createObject(), a reference to the returned object must be held so that + it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, + since setting the Item parent does not change object ownership; only the graphical parent is changed. + + As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a + map of initial property values for the created object. These values are applied before object + creation is finalized. (This is more efficient than setting property values after object creation, + particularly where large sets of property values are defined, and also allows property bindings + to be set up before the object is created.) + + The \a properties argument is specified as a map of property-value items. For example, the code + below creates an object with initial \c x and \c y values of 100 and 200, respectively: + + \js + var component = Qt.createComponent("Button.qml"); + if (component.status == Component.Ready) + component.createObject(parent, {"x": 100, "y": 100}); + \endjs + + Dynamically created instances can be deleted with the \c destroy() method. + See \l {Dynamic Object Management in QML} for more information. +*/ +void QQmlComponent::createObject(QQmlV8Function *args) +{ + Q_D(QQmlComponent); + Q_ASSERT(d->engine); + Q_ASSERT(args); + + QObject *parent = 0; + v8::Local<v8::Object> valuemap; + + if (args->Length() >= 1) + parent = args->engine()->toQObject((*args)[0]); + + if (args->Length() >= 2) { + v8::Local<v8::Value> v = (*args)[1]; + if (!v->IsObject() || v->IsArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + args->returnValue(v8::Null()); + return; + } + valuemap = v8::Local<v8::Object>::Cast(v); + } + + QV8Engine *v8engine = args->engine(); + + QQmlContext *ctxt = creationContext(); + if (!ctxt) ctxt = d->engine->rootContext(); + + QObject *rv = beginCreate(ctxt); + + if (!rv) { + args->returnValue(v8::Null()); + return; + } + + QQmlComponent_setQmlParent(rv, parent); + + v8::Handle<v8::Value> ov = v8engine->newQObject(rv); + Q_ASSERT(ov->IsObject()); + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov); + + if (!valuemap.IsEmpty()) { + QQmlComponentExtension *e = componentExtension(v8engine); + // Try catch isn't needed as the function itself is loaded with try/catch + v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal()); + v8::Handle<v8::Value> args[] = { object, valuemap }; + v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args); + } + + d->completeCreate(); + + Q_ASSERT(QQmlData::get(rv)); + QQmlData::get(rv)->setImplicitDestructible(); + + if (!rv) + args->returnValue(v8::Null()); + else + args->returnValue(object); +} + +/*! + \qmlmethod object Component::incubateObject(Item parent, object properties, enum mode) + + Creates an incubator for instance of this component. Incubators allow new component + instances to be instantiated asynchronously and not cause freezes in the UI. + + The \a parent argument specifies the parent the created instance will have. Omitting the + parameter or passing null will create anobject with no parent. In this case, a reference + to the created object must be maintained by the application of the object will eventually + be garbage collected. + + The \a properties argument is specified as a map of property-value items which will be + set on the created object during its construction. \a mode may be Qt.Synchronous or + Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously. + The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified, + the incubator may create the object asynchronously. This happens if the component calling + incubateObject() is itself being created asynchronously. + + All three arguments are optional. + + If successful, the method returns an incubator, otherwise null. The incubator has the following + properties: + + \list + \i status The status of the incubator. Valid values are Component.Ready, Component.Loading and + Component.Error. + \i object The created object instance. Will only be available once the incubator is in the + Ready status. + \i onStatusChanged Specifies a callback function to be invoked when the status changes. The + status is passed as a parameter to the callback. + \i forceCompletion() Call to complete incubation synchronously. + \endlist + + The following example demonstrates how to use an incubator: + + \js + var component = Qt.createComponent("Button.qml"); + + var incubator = component.incubateObject(parent, { x: 10, y: 10 }); + if (incubator.status != Component.Ready) { + incubator.onStatusChanged = function(status) { + if (status == Component.Ready) { + print ("Object", incubator.object, "is now ready!"); + } + } + } else { + print ("Object", incubator.object, "is ready immediately!"); + } + \endjs +*/ + +void QQmlComponent::incubateObject(QQmlV8Function *args) +{ + Q_D(QQmlComponent); + Q_ASSERT(d->engine); + Q_UNUSED(d); + Q_ASSERT(args); + + QObject *parent = 0; + v8::Local<v8::Object> valuemap; + QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous; + + if (args->Length() >= 1) + parent = args->engine()->toQObject((*args)[0]); + + if (args->Length() >= 2) { + v8::Local<v8::Value> v = (*args)[1]; + if (v->IsNull()) { + } else if (!v->IsObject() || v->IsArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + args->returnValue(v8::Null()); + return; + } else { + valuemap = v8::Local<v8::Object>::Cast(v); + } + } + + if (args->Length() >= 3) { + quint32 v = (*args)[2]->Uint32Value(); + if (v == 0) + mode = QQmlIncubator::Asynchronous; + else if (v == 1) + mode = QQmlIncubator::AsynchronousIfNested; + } + + QQmlComponentExtension *e = componentExtension(args->engine()); + + QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode); + v8::Local<v8::Object> o = e->incubationConstructor->NewInstance(); + o->SetExternalResource(r); + + if (!valuemap.IsEmpty()) { + r->valuemap = qPersistentNew(valuemap); + r->qmlGlobal = qPersistentNew(args->qmlGlobal()); + } + r->parent = parent; + r->me = qPersistentNew(o); + + create(*r, creationContext()); + + if (r->status() == QQmlIncubator::Null) { + r->dispose(); + args->returnValue(v8::Null()); + } else { + args->returnValue(o); + } +} + +// XXX used by QSGLoader +void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate) +{ + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + QV8Engine *v8engine = ep->v8engine(); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate); + Q_ASSERT(ov->IsObject()); + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov); + + if (!valuemap.IsEmpty()) { + QQmlComponentExtension *e = componentExtension(v8engine); + // Try catch isn't needed as the function itself is loaded with try/catch + v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal); + v8::Handle<v8::Value> args[] = { object, valuemap }; + v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args); + } + + QQmlData *ddata = QQmlData::get(toCreate); + Q_ASSERT(ddata); + ddata->setImplicitDestructible(); +} + + +QQmlComponentExtension::QQmlComponentExtension(QV8Engine *engine) +{ + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine)); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetInternalFieldCount(1); + ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"), + QV8IncubatorResource::StatusChangedGetter, + QV8IncubatorResource::StatusChangedSetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("status"), + QV8IncubatorResource::StatusGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("object"), + QV8IncubatorResource::ObjectGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"), + QV8IncubatorResource::ForceCompletionGetter); + incubationConstructor = qPersistentNew(ft->GetFunction()); + } + + { +#define INITIALPROPERTIES_SOURCE \ + "(function(object, values) {"\ + "try {"\ + "for(var property in values) {" \ + "try {"\ + "var properties = property.split(\".\");"\ + "var o = object;"\ + "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ + "o = o[properties[ii]];"\ + "}"\ + "o[properties[properties.length - 1]] = values[property];"\ + "} catch(e) {}"\ + "}"\ + "} catch(e) {}"\ + "})" + initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE))); +#undef INITIALPROPERTIES_SOURCE + } +} + +v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); + return r->engine->newQObject(r->object()); +} + +v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); + return componentExtension(r->engine)->forceCompletion; +} + +v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args) +{ + QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This()); + if (!r) + V8THROW_TYPE("Not an incubator object"); + + r->forceCompletion(); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); + return v8::Integer::NewFromUnsigned(r->status()); +} + +v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>, + const v8::AccessorInfo& info) +{ + return info.This()->GetInternalField(0); +} + +void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + info.This()->SetInternalField(0, value); +} + +QQmlComponentExtension::~QQmlComponentExtension() +{ + qPersistentDispose(incubationConstructor); + qPersistentDispose(initialProperties); + qPersistentDispose(forceCompletion); +} + +QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m) +: QV8ObjectResource(engine), QQmlIncubator(m) +{ +} + +void QV8IncubatorResource::setInitialState(QObject *o) +{ + QQmlComponent_setQmlParent(o, parent); + + if (!valuemap.IsEmpty()) { + QQmlComponentExtension *e = componentExtension(engine); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal); + v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap }; + v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args); + + qPersistentDispose(valuemap); + qPersistentDispose(qmlGlobal); + } +} + +void QV8IncubatorResource::dispose() +{ + qPersistentDispose(valuemap); + qPersistentDispose(qmlGlobal); + // No further status changes are forthcoming, so we no long need a self reference + qPersistentDispose(me); +} + +void QV8IncubatorResource::statusChanged(Status s) +{ + if (s == Ready) { + Q_ASSERT(QQmlData::get(object())); + QQmlData::get(object())->setImplicitDestructible(); + } + + if (!me.IsEmpty()) { // Will be false in synchronous mode + v8::HandleScope scope; + v8::Local<v8::Value> callback = me->GetInternalField(0); + + if (!callback.IsEmpty() && !callback->IsUndefined()) { + + if (callback->IsFunction()) { + v8::Context::Scope context_scope(engine->context()); + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback); + v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) }; + v8::TryCatch tc; + f->Call(me, 1, args); + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()), + error); + } + } + } + } + + if (s == Ready || s == Error) + dispose(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h new file mode 100644 index 0000000000..1265fb1c7d --- /dev/null +++ b/src/qml/qml/qqmlcomponent.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCOMPONENT_H +#define QQMLCOMPONENT_H + +#include <QtQml/qqml.h> +#include <QtQml/qqmlerror.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QByteArray; +class QQmlEngine; +class QQmlComponent; +class QQmlIncubator; +class QQmlV8Function; +class QQmlCompiledData; +class QQmlComponentPrivate; +class QQmlComponentAttached; + +class Q_QML_EXPORT QQmlComponent : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlComponent) + + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl url READ url CONSTANT) + +public: + QQmlComponent(QObject *parent = 0); + QQmlComponent(QQmlEngine *, QObject *parent=0); + QQmlComponent(QQmlEngine *, const QString &fileName, QObject *parent = 0); + QQmlComponent(QQmlEngine *, const QUrl &url, QObject *parent = 0); + virtual ~QQmlComponent(); + + Q_ENUMS(Status) + enum Status { Null, Ready, Loading, Error }; + Status status() const; + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + QList<QQmlError> errors() const; + Q_INVOKABLE QString errorString() const; + + qreal progress() const; + + QUrl url() const; + + virtual QObject *create(QQmlContext *context = 0); + virtual QObject *beginCreate(QQmlContext *); + virtual void completeCreate(); + + void create(QQmlIncubator &, QQmlContext *context = 0, + QQmlContext *forContext = 0); + + QQmlContext *creationContext() const; + + static QQmlComponentAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void loadUrl(const QUrl &url); + void setData(const QByteArray &, const QUrl &baseUrl); + +Q_SIGNALS: + void statusChanged(QQmlComponent::Status); + void progressChanged(qreal); + +protected: + QQmlComponent(QQmlComponentPrivate &dd, QObject* parent); + Q_INVOKABLE void createObject(QQmlV8Function *); + Q_INVOKABLE void incubateObject(QQmlV8Function *); + +private: + QQmlComponent(QQmlEngine *, QQmlCompiledData *, int, QObject *parent); + + Q_DISABLE_COPY(QQmlComponent) + friend class QQmlVME; + friend class QQmlTypeData; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlComponent::Status) +QML_DECLARE_TYPE(QQmlComponent) +QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQMLCOMPONENT_H diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h new file mode 100644 index 0000000000..731fb6a8a7 --- /dev/null +++ b/src/qml/qml/qqmlcomponent_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCOMPONENT_P_H +#define QQMLCOMPONENT_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 "qqmlcomponent.h" + +#include <private/qv8_p.h> +#include "qqmlengine_p.h" +#include "qqmltypeloader_p.h" +#include <private/qbitfield_p.h> +#include "qqmlvme_p.h" +#include "qqmlerror.h" +#include "qqml.h" + +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QList> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +class QQmlComponent; +class QQmlEngine; +class QQmlCompiledData; + +class QQmlComponentAttached; +class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback +{ + Q_DECLARE_PUBLIC(QQmlComponent) + +public: + QQmlComponentPrivate() : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0) {} + + QObject *beginCreate(QQmlContextData *); + void completeCreate(); + void initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate); + + QQmlTypeData *typeData; + virtual void typeDataReady(QQmlTypeData *); + virtual void typeDataProgress(QQmlTypeData *, qreal); + + void fromTypeData(QQmlTypeData *data); + + QUrl url; + qreal progress; + + int start; + QQmlCompiledData *cc; + + struct ConstructionState { + ConstructionState() : completePending(false) {} + + QQmlVME vme; + QList<QQmlError> errors; + bool completePending; + }; + ConstructionState state; + + static void beginDeferred(QQmlEnginePrivate *enginePriv, QObject *object, + ConstructionState *state); + static void complete(QQmlEnginePrivate *enginePriv, ConstructionState *state); + + QQmlEngine *engine; + QQmlGuardedContextData creationContext; + + void clear(); + + static QQmlComponentPrivate *get(QQmlComponent *c) { + return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(c)); + } +}; + +QT_END_NAMESPACE + +#endif // QQMLCOMPONENT_P_H diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h new file mode 100644 index 0000000000..09d111c227 --- /dev/null +++ b/src/qml/qml/qqmlcomponentattached_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCOMPONENTATTACHED_P_H +#define QQMLCOMPONENTATTACHED_P_H + +#include <QtQml/qqml.h> +#include <QtCore/QObject> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class Q_AUTOTEST_EXPORT QQmlComponentAttached : public QObject +{ + Q_OBJECT +public: + QQmlComponentAttached(QObject *parent = 0); + ~QQmlComponentAttached(); + + void add(QQmlComponentAttached **a) { + prev = a; next = *a; *a = this; + if (next) next->prev = &next; + } + void rem() { + if (next) next->prev = prev; + *prev = next; + next = 0; prev = 0; + } + QQmlComponentAttached **prev; + QQmlComponentAttached *next; + +Q_SIGNALS: + void completed(); + void destruction(); + +private: + friend class QQmlVME; + friend class QQmlContextData; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLCOMPONENTATTACHED_P_H diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp new file mode 100644 index 0000000000..38acc0b0c3 --- /dev/null +++ b/src/qml/qml/qqmlcontext.cpp @@ -0,0 +1,811 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcontext.h" +#include "qqmlcontext_p.h" +#include "qqmlcomponentattached_p.h" + +#include "qqmlcomponent_p.h" +#include "qqmlexpression_p.h" +#include "qqmlengine_p.h" +#include "qqmlengine.h" +#include "qqmlinfo.h" +#include <private/qv4bindings_p.h> +#include <private/qv8bindings_p.h> + +#include <qjsengine.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QQmlContextPrivate::QQmlContextPrivate() +: data(0), notifyIndex(-1) +{ +} + +/*! + \class QQmlContext + \since 4.7 + \brief The QQmlContext class defines a context within a QML engine. + \mainclass + + Contexts allow data to be exposed to the QML components instantiated by the + QML engine. + + Each QQmlContext contains a set of properties, distinct from its QObject + properties, that allow data to be explicitly bound to a context by name. The + context properties are defined and updated by calling + QQmlContext::setContextProperty(). The following example shows a Qt model + being bound to a context and then accessed from a QML file. + + \code + QQmlEngine engine; + QStringListModel modelData; + QQmlContext *context = new QQmlContext(engine.rootContext()); + context->setContextProperty("myModel", &modelData); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl()); + QObject *window = component.create(context); + \endcode + + Note it is the responsibility of the creator to delete any QQmlContext it + constructs. If the \c context object in the example is no longer needed when the + \c window component instance is destroyed, the \c context must be destroyed explicitly. + The simplest way to ensure this is to set \c window as the parent of \c context. + + To simplify binding and maintaining larger data sets, a context object can be set + on a QQmlContext. All the properties of the context object are available + by name in the context, as though they were all individually added through calls + to QQmlContext::setContextProperty(). Changes to the property's values are + detected through the property's notify signal. Setting a context object is both + faster and easier than manually adding and maintaing context property values. + + The following example has the same effect as the previous one, but it uses a context + object. + + \code + class MyDataSet : ... { + ... + Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged) + ... + }; + + MyDataSet myDataSet; + QQmlEngine engine; + QQmlContext *context = new QQmlContext(engine.rootContext()); + context->setContextObject(&myDataSet); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl()); + component.create(context); + \endcode + + All properties added explicitly by QQmlContext::setContextProperty() take + precedence over the context object's properties. + + \section2 The Context Hierarchy + + Contexts form a hierarchy. The root of this hierarchy is the QML engine's + \l {QQmlEngine::rootContext()}{root context}. Child contexts inherit + the context properties of their parents; if a child context sets a context property + that already exists in its parent, the new context property overrides that of the + parent. + + The following example defines two contexts - \c context1 and \c context2. The + second context overrides the "b" context property inherited from the first with a + new value. + + \code + QQmlEngine engine; + QQmlContext *context1 = new QQmlContext(engine.rootContext()); + QQmlContext *context2 = new QQmlContext(context1); + + context1->setContextProperty("a", 12); + context1->setContextProperty("b", 12); + + context2->setContextProperty("b", 15); + \endcode + + While QML objects instantiated in a context are not strictly owned by that + context, their bindings are. If a context is destroyed, the property bindings of + outstanding QML objects will stop evaluating. + + \warning Setting the context object or adding new context properties after an object + has been created in that context is an expensive operation (essentially forcing all bindings + to reevaluate). Thus whenever possible you should complete "setup" of the context + before using it to create any objects. + + \sa {Using QML Bindings in C++ Applications} +*/ + +/*! \internal */ +QQmlContext::QQmlContext(QQmlEngine *e, bool) +: QObject(*(new QQmlContextPrivate)) +{ + Q_D(QQmlContext); + d->data = new QQmlContextData(this); + + d->data->engine = e; +} + +/*! + Create a new QQmlContext as a child of \a engine's root context, and the + QObject \a parent. +*/ +QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) +: QObject(*(new QQmlContextPrivate), parent) +{ + Q_D(QQmlContext); + d->data = new QQmlContextData(this); + + d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0); +} + +/*! + Create a new QQmlContext with the given \a parentContext, and the + QObject \a parent. +*/ +QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent) +: QObject(*(new QQmlContextPrivate), parent) +{ + Q_D(QQmlContext); + d->data = new QQmlContextData(this); + + d->data->setParent(parentContext?QQmlContextData::get(parentContext):0); +} + +/*! + \internal +*/ +QQmlContext::QQmlContext(QQmlContextData *data) +: QObject(*(new QQmlContextPrivate), 0) +{ + Q_D(QQmlContext); + d->data = data; +} + +/*! + Destroys the QQmlContext. + + Any expressions, or sub-contexts dependent on this context will be + invalidated, but not destroyed (unless they are parented to the QQmlContext + object). + */ +QQmlContext::~QQmlContext() +{ + Q_D(QQmlContext); + + if (!d->data->isInternal) + d->data->destroy(); +} + +/*! + Returns whether the context is valid. + + To be valid, a context must have a engine, and it's contextObject(), if any, + must not have been deleted. +*/ +bool QQmlContext::isValid() const +{ + Q_D(const QQmlContext); + return d->data && d->data->isValid(); +} + +/*! + Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the + QQmlEngine was destroyed. +*/ +QQmlEngine *QQmlContext::engine() const +{ + Q_D(const QQmlContext); + return d->data->engine; +} + +/*! + Return the context's parent QQmlContext, or 0 if this context has no + parent or if the parent has been destroyed. +*/ +QQmlContext *QQmlContext::parentContext() const +{ + Q_D(const QQmlContext); + return d->data->parent?d->data->parent->asQQmlContext():0; +} + +/*! + Return the context object, or 0 if there is no context object. +*/ +QObject *QQmlContext::contextObject() const +{ + Q_D(const QQmlContext); + return d->data->contextObject; +} + +/*! + Set the context \a object. +*/ +void QQmlContext::setContextObject(QObject *object) +{ + Q_D(QQmlContext); + + QQmlContextData *data = d->data; + + if (data->isInternal) { + qWarning("QQmlContext: Cannot set context object for internal context."); + return; + } + + if (!isValid()) { + qWarning("QQmlContext: Cannot set context object on invalid context."); + return; + } + + data->contextObject = object; +} + +/*! + Set a the \a value of the \a name property on this context. +*/ +void QQmlContext::setContextProperty(const QString &name, const QVariant &value) +{ + Q_D(QQmlContext); + if (d->notifyIndex == -1) + d->notifyIndex = this->metaObject()->methodCount(); + + QQmlContextData *data = d->data; + + if (data->isInternal) { + qWarning("QQmlContext: Cannot set property on internal context."); + return; + } + + if (!isValid()) { + qWarning("QQmlContext: Cannot set property on invalid context."); + return; + } + + if (data->engine) { + bool ok; + QObject *o = QQmlEnginePrivate::get(data->engine)->toQObject(value, &ok); + if (ok) { + setContextProperty(name, o); + return; + } + } + + if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache(); + + int idx = data->propertyNames->value(name); + if (idx == -1) { + data->propertyNames->add(name, data->idValueCount + d->propertyValues.count()); + d->propertyValues.append(value); + + data->refreshExpressions(); + } else { + d->propertyValues[idx] = value; + QMetaObject::activate(this, idx + d->notifyIndex, 0); + } +} + +/*! + Set the \a value of the \a name property on this context. + + QQmlContext does \bold not take ownership of \a value. +*/ +void QQmlContext::setContextProperty(const QString &name, QObject *value) +{ + Q_D(QQmlContext); + if (d->notifyIndex == -1) + d->notifyIndex = this->metaObject()->methodCount(); + + QQmlContextData *data = d->data; + + if (data->isInternal) { + qWarning("QQmlContext: Cannot set property on internal context."); + return; + } + + if (!isValid()) { + qWarning("QQmlContext: Cannot set property on invalid context."); + return; + } + + if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache(); + int idx = data->propertyNames->value(name); + + if (idx == -1) { + data->propertyNames->add(name, data->idValueCount + d->propertyValues.count()); + d->propertyValues.append(QVariant::fromValue(value)); + + data->refreshExpressions(); + } else { + d->propertyValues[idx] = QVariant::fromValue(value); + QMetaObject::activate(this, idx + d->notifyIndex, 0); + } +} + +/*! + Returns the value of the \a name property for this context + as a QVariant. + */ +QVariant QQmlContext::contextProperty(const QString &name) const +{ + Q_D(const QQmlContext); + QVariant value; + int idx = -1; + + QQmlContextData *data = d->data; + + if (data->propertyNames) + idx = data->propertyNames->value(name); + + if (idx == -1) { + QByteArray utf8Name = name.toUtf8(); + if (data->contextObject) { + QObject *obj = data->contextObject; + QQmlPropertyData local; + QQmlPropertyData *property = + QQmlPropertyCache::property(data->engine, obj, name, local); + + if (property) value = obj->metaObject()->property(property->coreIndex).read(obj); + } + if (!value.isValid() && parentContext()) + value = parentContext()->contextProperty(name); + } else { + if (idx >= d->propertyValues.count()) + value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data()); + else + value = d->propertyValues[idx]; + } + + return value; +} + +/*! +Returns the name of \a object in this context, or an empty string if \a object +is not named in the context. Objects are named by setContextProperty(), or by ids in +the case of QML created contexts. + +If the object has multiple names, the first is returned. +*/ +QString QQmlContext::nameForObject(QObject *object) const +{ + Q_D(const QQmlContext); + + return d->data->findObjectId(object); +} + +/*! + Resolves the URL \a src relative to the URL of the + containing component. + + \sa QQmlEngine::baseUrl(), setBaseUrl() +*/ +QUrl QQmlContext::resolvedUrl(const QUrl &src) +{ + Q_D(QQmlContext); + return d->data->resolvedUrl(src); +} + +QUrl QQmlContextData::resolvedUrl(const QUrl &src) +{ + QQmlContextData *ctxt = this; + + if (src.isRelative() && !src.isEmpty()) { + if (ctxt) { + while(ctxt) { + if(ctxt->url.isValid()) + break; + else + ctxt = ctxt->parent; + } + + if (ctxt) + return ctxt->url.resolved(src); + else if (engine) + return engine->baseUrl().resolved(src); + } + return QUrl(); + } else { + return src; + } +} + + +/*! + Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl. + + Calling this function will override the url of the containing + component used by default. + + \sa resolvedUrl() +*/ +void QQmlContext::setBaseUrl(const QUrl &baseUrl) +{ + Q_D(QQmlContext); + + d->data->url = baseUrl; + d->data->urlString = baseUrl.toString(); +} + +/*! + Returns the base url of the component, or the containing component + if none is set. +*/ +QUrl QQmlContext::baseUrl() const +{ + Q_D(const QQmlContext); + const QQmlContextData* data = d->data; + while (data && data->url.isEmpty()) + data = data->parent; + + if (data) + return data->url; + else + return QUrl(); +} + +int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop) +{ + QQmlContext *context = static_cast<QQmlContext*>(prop->object); + QQmlContextPrivate *d = QQmlContextPrivate::get(context); + int contextProperty = (int)(quintptr)prop->data; + + if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { + return 0; + } else { + return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count(); + } +} + +QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index) +{ + QQmlContext *context = static_cast<QQmlContext*>(prop->object); + QQmlContextPrivate *d = QQmlContextPrivate::get(context); + int contextProperty = (int)(quintptr)prop->data; + + if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { + return 0; + } else { + return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index); + } +} + + +QQmlContextData::QQmlContextData() +: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), + isPragmaLibraryContext(false), unresolvedNames(false), publicContext(0), activeVMEData(0), + propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), + expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), + componentAttached(0), v4bindings(0), v8bindings(0) +{ +} + +QQmlContextData::QQmlContextData(QQmlContext *ctxt) +: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), + isPragmaLibraryContext(false), unresolvedNames(false), publicContext(ctxt), activeVMEData(0), + propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), + expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), + componentAttached(0), v4bindings(0), v8bindings(0) +{ +} + +void QQmlContextData::invalidate() +{ + while (componentAttached) { + QQmlComponentAttached *a = componentAttached; + componentAttached = a->next; + if (componentAttached) componentAttached->prev = &componentAttached; + + a->next = 0; + a->prev = 0; + + emit a->destruction(); + } + + while (childContexts) { + if (childContexts->ownedByParent) { + childContexts->destroy(); + } else { + childContexts->invalidate(); + } + } + + if (prevChild) { + *prevChild = nextChild; + if (nextChild) nextChild->prevChild = prevChild; + nextChild = 0; + prevChild = 0; + } + + engine = 0; + parent = 0; +} + +void QQmlContextData::clearContext() +{ + if (engine) { + while (componentAttached) { + QQmlComponentAttached *a = componentAttached; + componentAttached = a->next; + if (componentAttached) componentAttached->prev = &componentAttached; + + a->next = 0; + a->prev = 0; + + emit a->destruction(); + } + } + + QQmlAbstractExpression *expression = expressions; + while (expression) { + QQmlAbstractExpression *nextExpression = expression->m_nextExpression; + + expression->m_prevExpression = 0; + expression->m_nextExpression = 0; + + expression->setContext(0); + + expression = nextExpression; + } + expressions = 0; +} + +void QQmlContextData::destroy() +{ + if (linkedContext) + linkedContext->destroy(); + + if (engine) invalidate(); + + clearContext(); + + while (contextObjects) { + QQmlData *co = contextObjects; + contextObjects = contextObjects->nextContextObject; + + co->context = 0; + co->outerContext = 0; + co->nextContextObject = 0; + co->prevContextObject = 0; + } + + QQmlGuardedContextData *contextGuard = contextGuards; + while (contextGuard) { + QQmlGuardedContextData *next = contextGuard->m_next; + contextGuard->m_next = 0; + contextGuard->m_prev = 0; + contextGuard->m_contextData = 0; + contextGuard = next; + } + contextGuards = 0; + + if (propertyNames) + propertyNames->release(); + + if (imports) + imports->release(); + + if (v4bindings) + v4bindings->release(); + + if (v8bindings) + v8bindings->release(); + + for (int ii = 0; ii < importedScripts.count(); ++ii) { + qPersistentDispose(importedScripts[ii]); + } + + delete [] idValues; + + if (isInternal) + delete publicContext; + + delete this; +} + +void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) +{ + if (p) { + parent = p; + engine = p->engine; + nextChild = p->childContexts; + if (nextChild) nextChild->prevChild = &nextChild; + prevChild = &p->childContexts; + p->childContexts = this; + ownedByParent = parentTakesOwnership; + } +} + +void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression) +{ + QQmlAbstractExpression::DeleteWatcher w(expression); + + if (expression->m_nextExpression) + refreshExpressionsRecursive(expression->m_nextExpression); + + if (!w.wasDeleted()) + expression->refresh(); +} + +static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh) +{ + return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames); +} + +void QQmlContextData::refreshExpressionsRecursive(bool isGlobal) +{ + // For efficiency, we try and minimize the number of guards we have to create + if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) { + QQmlGuardedContextData guard(this); + + if (childContexts) + childContexts->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (nextChild) + nextChild->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (expressions_to_run(this, isGlobal)) + refreshExpressionsRecursive(expressions); + + } else if (expressions_to_run(this, isGlobal)) { + + refreshExpressionsRecursive(expressions); + + } else if (nextChild && childContexts) { + + QQmlGuardedContextData guard(this); + + childContexts->refreshExpressionsRecursive(isGlobal); + + if (!guard.isNull() && nextChild) + nextChild->refreshExpressionsRecursive(isGlobal); + + } else if (nextChild) { + + nextChild->refreshExpressionsRecursive(isGlobal); + + } else if (childContexts) { + + childContexts->refreshExpressionsRecursive(isGlobal); + + } +} + +// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all +// context-tree dependent caches in the expressions, and should occur every time the context tree +// *structure* (not values) changes. +void QQmlContextData::refreshExpressions() +{ + bool isGlobal = (parent == 0); + + // For efficiency, we try and minimize the number of guards we have to create + if (expressions_to_run(this, isGlobal) && childContexts) { + QQmlGuardedContextData guard(this); + + childContexts->refreshExpressionsRecursive(isGlobal); + + if (!guard.isNull() && expressions_to_run(this, isGlobal)) + refreshExpressionsRecursive(expressions); + + } else if (expressions_to_run(this, isGlobal)) { + + refreshExpressionsRecursive(expressions); + + } else if (childContexts) { + + childContexts->refreshExpressionsRecursive(isGlobal); + + } +} + +void QQmlContextData::addObject(QObject *o) +{ + QQmlData *data = QQmlData::get(o, true); + + Q_ASSERT(data->context == 0); + + data->context = this; + data->outerContext = this; + + data->nextContextObject = contextObjects; + if (data->nextContextObject) + data->nextContextObject->prevContextObject = &data->nextContextObject; + data->prevContextObject = &contextObjects; + contextObjects = data; +} + +void QQmlContextData::setIdProperty(int idx, QObject *obj) +{ + idValues[idx] = obj; + idValues[idx].context = this; +} + +void QQmlContextData::setIdPropertyData(QQmlIntegerCache *data) +{ + Q_ASSERT(!propertyNames); + propertyNames = data; + propertyNames->addref(); + + idValueCount = data->count(); + idValues = new ContextGuard[idValueCount]; +} + +QString QQmlContextData::findObjectId(const QObject *obj) const +{ + if (!propertyNames) + return QString(); + + for (int ii = 0; ii < idValueCount; ii++) { + if (idValues[ii] == obj) + return propertyNames->findId(ii); + } + + if (publicContext) { + QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext); + for (int ii = 0; ii < p->propertyValues.count(); ++ii) + if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj)) + return propertyNames->findId(ii); + } + + if (linkedContext) + return linkedContext->findObjectId(obj); + return QString(); +} + +QQmlContext *QQmlContextData::asQQmlContext() +{ + if (!publicContext) + publicContext = new QQmlContext(this); + return publicContext; +} + +QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() +{ + return QQmlContextPrivate::get(asQQmlContext()); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h new file mode 100644 index 0000000000..f6d8aa1d3a --- /dev/null +++ b/src/qml/qml/qqmlcontext.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCONTEXT_H +#define QQMLCONTEXT_H + +#include <QtCore/qurl.h> +#include <QtCore/qobject.h> +#include <QtQml/qjsvalue.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QString; +class QQmlEngine; +class QQmlRefCount; +class QQmlContextPrivate; +class QQmlCompositeTypeData; +class QQmlContextData; + +class Q_QML_EXPORT QQmlContext : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlContext) + +public: + QQmlContext(QQmlEngine *parent, QObject *objParent=0); + QQmlContext(QQmlContext *parent, QObject *objParent=0); + virtual ~QQmlContext(); + + bool isValid() const; + + QQmlEngine *engine() const; + QQmlContext *parentContext() const; + + QObject *contextObject() const; + void setContextObject(QObject *); + + QVariant contextProperty(const QString &) const; + void setContextProperty(const QString &, QObject *); + void setContextProperty(const QString &, const QVariant &); + + QString nameForObject(QObject *) const; + + QUrl resolvedUrl(const QUrl &); + + void setBaseUrl(const QUrl &); + QUrl baseUrl() const; + +private: + friend class QQmlVME; + friend class QQmlEngine; + friend class QQmlEnginePrivate; + friend class QQmlExpression; + friend class QQmlExpressionPrivate; + friend class QQmlComponent; + friend class QQmlComponentPrivate; + friend class QQmlScriptPrivate; + friend class QQmlBoundSignalProxy; + friend class QQmlContextData; + QQmlContext(QQmlContextData *); + QQmlContext(QQmlEngine *, bool); + Q_DISABLE_COPY(QQmlContext) +}; +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QList<QObject*>) + +QT_END_HEADER + +#endif // QQMLCONTEXT_H diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h new file mode 100644 index 0000000000..d10543bde5 --- /dev/null +++ b/src/qml/qml/qqmlcontext_p.h @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCONTEXT_P_H +#define QQMLCONTEXT_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 "qqmlcontext.h" + +#include "qqmldata_p.h" +#include "qqmlintegercache_p.h" +#include "qqmltypenamecache_p.h" +#include "qqmlnotifier_p.h" +#include "qqmllist.h" +#include "qqmlscript_p.h" + +#include <QtCore/qhash.h> +#include <QtQml/qjsvalue.h> +#include <QtCore/qset.h> + +#include <private/qobject_p.h> +#include <private/qflagpointer_p.h> +#include <private/qqmlguard_p.h> + +#include <private/qv8_p.h> + + +QT_BEGIN_NAMESPACE + +class QV8Bindings; +class QQmlContext; +class QQmlExpression; +class QQmlEngine; +class QQmlExpression; +class QQmlExpressionPrivate; +class QQmlAbstractExpression; +class QV4Bindings; +class QQmlContextData; + +class QQmlContextPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlContext) +public: + QQmlContextPrivate(); + + QQmlContextData *data; + + QList<QVariant> propertyValues; + int notifyIndex; + + static QQmlContextPrivate *get(QQmlContext *context) { + return static_cast<QQmlContextPrivate *>(QObjectPrivate::get(context)); + } + static QQmlContext *get(QQmlContextPrivate *context) { + return static_cast<QQmlContext *>(context->q_func()); + } + + // Only used for debugging + QList<QPointer<QObject> > instances; + + static int context_count(QQmlListProperty<QObject> *); + static QObject *context_at(QQmlListProperty<QObject> *, int); +}; + +class QQmlVME; +class QQmlComponentAttached; +class QQmlGuardedContextData; +class Q_QML_EXPORT QQmlContextData +{ +public: + QQmlContextData(); + QQmlContextData(QQmlContext *); + void clearContext(); + void destroy(); + void invalidate(); + + inline bool isValid() const { + return engine && (!isInternal || !contextObject || !QObjectPrivate::get(contextObject)->wasDeleted); + } + + // My parent context and engine + QQmlContextData *parent; + QQmlEngine *engine; + + void setParent(QQmlContextData *, bool parentTakesOwnership = false); + void refreshExpressions(); + + void addObject(QObject *); + + QUrl resolvedUrl(const QUrl &); + + // My containing QQmlContext. If isInternal is true this owns publicContext. + // If internal is false publicContext owns this. + QQmlContext *asQQmlContext(); + QQmlContextPrivate *asQQmlContextPrivate(); + quint32 isInternal:1; + quint32 ownedByParent:1; // unrelated to isInternal; parent context deletes children if true. + quint32 isJSContext:1; + quint32 isPragmaLibraryContext:1; + quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name + quint32 dummy:28; + QQmlContext *publicContext; + + // VME data that is constructing this context if any + void *activeVMEData; + + // Property name cache + QQmlIntegerCache *propertyNames; + + // Context object + QObject *contextObject; + + // Any script blocks that exist on this context + QList<v8::Persistent<v8::Object> > importedScripts; + + // Context base url + QUrl url; + QString urlString; + + // List of imports that apply to this context + QQmlTypeNameCache *imports; + + // My children + QQmlContextData *childContexts; + + // My peers in parent's childContexts list + QQmlContextData *nextChild; + QQmlContextData **prevChild; + + // Expressions that use this context + QQmlAbstractExpression *expressions; + + // Doubly-linked list of objects that are owned by this context + QQmlData *contextObjects; + + // Doubly-linked list of context guards (XXX merge with contextObjects) + QQmlGuardedContextData *contextGuards; + + // id guards + struct ContextGuard : public QQmlGuard<QObject> + { + inline ContextGuard(); + inline ContextGuard &operator=(QObject *obj); + inline void objectDestroyed(QObject *); + + inline bool wasSet() const; + + QFlagPointer<QQmlContextData> context; + QQmlNotifier bindings; + }; + ContextGuard *idValues; + int idValueCount; + void setIdProperty(int, QObject *); + void setIdPropertyData(QQmlIntegerCache *); + + // Linked contexts. this owns linkedContext. + QQmlContextData *linkedContext; + + // Linked list of uses of the Component attached property in this + // context + QQmlComponentAttached *componentAttached; + + // Optimized binding objects. Needed for deferred properties. + QV4Bindings *v4bindings; + QV8Bindings *v8bindings; + + // Return the outermost id for obj, if any. + QString findObjectId(const QObject *obj) const; + + static QQmlContextData *get(QQmlContext *context) { + return QQmlContextPrivate::get(context)->data; + } + +private: + void refreshExpressionsRecursive(bool isGlobal); + void refreshExpressionsRecursive(QQmlAbstractExpression *); + ~QQmlContextData() {} +}; + +class QQmlGuardedContextData +{ +public: + inline QQmlGuardedContextData(); + inline QQmlGuardedContextData(QQmlContextData *); + inline ~QQmlGuardedContextData(); + + inline QQmlContextData *contextData(); + inline void setContextData(QQmlContextData *); + + inline bool isNull() const { return !m_contextData; } + + inline operator QQmlContextData*() const { return m_contextData; } + inline QQmlContextData* operator->() const { return m_contextData; } + inline QQmlGuardedContextData &operator=(QQmlContextData *d); + +private: + QQmlGuardedContextData &operator=(const QQmlGuardedContextData &); + QQmlGuardedContextData(const QQmlGuardedContextData &); + friend class QQmlContextData; + + inline void clear(); + + QQmlContextData *m_contextData; + QQmlGuardedContextData *m_next; + QQmlGuardedContextData **m_prev; +}; + +QQmlGuardedContextData::QQmlGuardedContextData() +: m_contextData(0), m_next(0), m_prev(0) +{ +} + +QQmlGuardedContextData::QQmlGuardedContextData(QQmlContextData *data) +: m_contextData(0), m_next(0), m_prev(0) +{ + setContextData(data); +} + +QQmlGuardedContextData::~QQmlGuardedContextData() +{ + clear(); +} + +void QQmlGuardedContextData::setContextData(QQmlContextData *contextData) +{ + clear(); + + if (contextData) { + m_contextData = contextData; + m_next = contextData->contextGuards; + if (m_next) m_next->m_prev = &m_next; + m_prev = &contextData->contextGuards; + contextData->contextGuards = this; + } +} + +QQmlContextData *QQmlGuardedContextData::contextData() +{ + return m_contextData; +} + +void QQmlGuardedContextData::clear() +{ + if (m_prev) { + *m_prev = m_next; + if (m_next) m_next->m_prev = m_prev; + m_contextData = 0; + m_next = 0; + m_prev = 0; + } +} + +QQmlGuardedContextData & +QQmlGuardedContextData::operator=(QQmlContextData *d) +{ + setContextData(d); + return *this; +} + +QQmlContextData::ContextGuard::ContextGuard() +: context(0) +{ +} + +QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject *obj) +{ + QQmlGuard<QObject>::operator=(obj); + context.setFlag(); + bindings.notify(); // For alias connections + return *this; +} + +void QQmlContextData::ContextGuard::objectDestroyed(QObject *) +{ + if (context->contextObject && !QObjectPrivate::get(context->contextObject)->wasDeleted) + bindings.notify(); +} + +bool QQmlContextData::ContextGuard::wasSet() const +{ + return context.flag(); +} + +QT_END_NAMESPACE + +#endif // QQMLCONTEXT_P_H diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp new file mode 100644 index 0000000000..f888b61e7d --- /dev/null +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcustomparser_p.h" +#include "qqmlcustomparser_p_p.h" + +#include "qqmlcompiler_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +using namespace QQmlScript; + +/*! + \class QQmlCustomParser + \brief The QQmlCustomParser class allows you to add new arbitrary types to QML. + \internal + + By subclassing QQmlCustomParser, you can add a parser for + building a particular type. + + The subclass must implement compile() and setCustomData(), and register + itself in the meta type system by calling the macro: + + \code + QML_REGISTER_CUSTOM_TYPE(Module, MajorVersion, MinorVersion, Name, TypeClass, ParserClass) + \endcode +*/ + +/* + \fn QByteArray QQmlCustomParser::compile(const QList<QQmlCustomParserProperty> & properties) + + The custom parser processes \a properties, and returns + a QByteArray containing data meaningful only to the + custom parser; the type engine will pass this same data to + setCustomData() when making an instance of the data. + + Errors must be reported via the error() functions. + + The QByteArray may be cached between executions of the system, so + it must contain correctly-serialized data (not, for example, + pointers to stack objects). +*/ + +/* + \fn void QQmlCustomParser::setCustomData(QObject *object, const QByteArray &data) + + This function sets \a object to have the properties defined + by \a data, which is a block of data previously returned by a call + to compile(). + + Errors should be reported using qmlInfo(object). + + The \a object will be an instance of the TypeClass specified by QML_REGISTER_CUSTOM_TYPE. +*/ + +QQmlCustomParserNode +QQmlCustomParserNodePrivate::fromObject(QQmlScript::Object *root) +{ + QQmlCustomParserNode rootNode; + rootNode.d->name = root->typeName; + rootNode.d->location = root->location.start; + + for (Property *p = root->properties.first(); p; p = root->properties.next(p)) { + rootNode.d->properties << fromProperty(p); + } + + if (root->defaultProperty) + rootNode.d->properties << fromProperty(root->defaultProperty); + + return rootNode; +} + +QQmlCustomParserProperty +QQmlCustomParserNodePrivate::fromProperty(QQmlScript::Property *p) +{ + QQmlCustomParserProperty prop; + prop.d->name = p->name().toString(); + prop.d->isList = p->values.isMany(); + prop.d->location = p->location.start; + + if (p->value) { + QQmlCustomParserNode node = fromObject(p->value); + QList<QQmlCustomParserProperty> props = node.properties(); + for (int ii = 0; ii < props.count(); ++ii) + prop.d->values << QVariant::fromValue(props.at(ii)); + } else { + for (QQmlScript::Value *v = p->values.first(); v; v = p->values.next(v)) { + v->type = QQmlScript::Value::Literal; + + if(v->object) { + QQmlCustomParserNode node = fromObject(v->object); + prop.d->values << QVariant::fromValue(node); + } else { + prop.d->values << QVariant::fromValue(v->value); + } + + } + } + + return prop; +} + +QQmlCustomParserNode::QQmlCustomParserNode() +: d(new QQmlCustomParserNodePrivate) +{ +} + +QQmlCustomParserNode::QQmlCustomParserNode(const QQmlCustomParserNode &other) +: d(new QQmlCustomParserNodePrivate) +{ + *this = other; +} + +QQmlCustomParserNode &QQmlCustomParserNode::operator=(const QQmlCustomParserNode &other) +{ + d->name = other.d->name; + d->properties = other.d->properties; + d->location = other.d->location; + return *this; +} + +QQmlCustomParserNode::~QQmlCustomParserNode() +{ + delete d; d = 0; +} + +QString QQmlCustomParserNode::name() const +{ + return d->name; +} + +QList<QQmlCustomParserProperty> QQmlCustomParserNode::properties() const +{ + return d->properties; +} + +QQmlScript::Location QQmlCustomParserNode::location() const +{ + return d->location; +} + +QQmlCustomParserProperty::QQmlCustomParserProperty() +: d(new QQmlCustomParserPropertyPrivate) +{ +} + +QQmlCustomParserProperty::QQmlCustomParserProperty(const QQmlCustomParserProperty &other) +: d(new QQmlCustomParserPropertyPrivate) +{ + *this = other; +} + +QQmlCustomParserProperty &QQmlCustomParserProperty::operator=(const QQmlCustomParserProperty &other) +{ + d->name = other.d->name; + d->isList = other.d->isList; + d->values = other.d->values; + d->location = other.d->location; + return *this; +} + +QQmlCustomParserProperty::~QQmlCustomParserProperty() +{ + delete d; d = 0; +} + +QString QQmlCustomParserProperty::name() const +{ + return d->name; +} + +bool QQmlCustomParserProperty::isList() const +{ + return d->isList; +} + +QQmlScript::Location QQmlCustomParserProperty::location() const +{ + return d->location; +} + +QList<QVariant> QQmlCustomParserProperty::assignedValues() const +{ + return d->values; +} + +void QQmlCustomParser::clearErrors() +{ + exceptions.clear(); +} + +/*! + Reports an error with the given \a description. + + This can only be used during the compile() step. For errors during setCustomData(), use qmlInfo(). + + An error is generated referring to the position of the element in the source file. +*/ +void QQmlCustomParser::error(const QString& description) +{ + Q_ASSERT(object); + QQmlError error; + QString exceptionDescription; + error.setLine(object->location.start.line); + error.setColumn(object->location.start.column); + error.setDescription(description); + exceptions << error; +} + +/*! + Reports an error in parsing \a prop, with the given \a description. + + An error is generated referring to the position of \a node in the source file. +*/ +void QQmlCustomParser::error(const QQmlCustomParserProperty& prop, const QString& description) +{ + QQmlError error; + QString exceptionDescription; + error.setLine(prop.location().line); + error.setColumn(prop.location().column); + error.setDescription(description); + exceptions << error; +} + +/*! + Reports an error in parsing \a node, with the given \a description. + + An error is generated referring to the position of \a node in the source file. +*/ +void QQmlCustomParser::error(const QQmlCustomParserNode& node, const QString& description) +{ + QQmlError error; + QString exceptionDescription; + error.setLine(node.location().line); + error.setColumn(node.location().column); + error.setDescription(description); + exceptions << error; +} + +/*! + If \a script is a simply enum expression (eg. Text.AlignLeft), + returns the integer equivalent (eg. 1). + + Otherwise, returns -1. +*/ +int QQmlCustomParser::evaluateEnum(const QByteArray& script) const +{ + return compiler->evaluateEnum(script); +} + +/*! + Resolves \a name to a type, or 0 if it is not a type. This can be used + to type-check object nodes. +*/ +const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const +{ + return compiler->resolveType(name); +} + +/*! + Rewrites \a value and returns an identifier that can be + used to construct the binding later. \a name + is used as the name of the rewritten function. +*/ +QQmlBinding::Identifier QQmlCustomParser::rewriteBinding(const QQmlScript::Variant &value, const QString& name) +{ + return compiler->rewriteBinding(value, name); +} + +/*! + Returns a rewritten \a handler. \a name + is used as the name of the rewritten function. +*/ +QString QQmlCustomParser::rewriteSignalHandler(const QQmlScript::Variant &value, const QString &name) +{ + return compiler->rewriteSignalHandler(value , name); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h new file mode 100644 index 0000000000..ecc4bae4c3 --- /dev/null +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCUSTOMPARSER_H +#define QQMLCUSTOMPARSER_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 "qqmlmetatype_p.h" +#include "qqmlerror.h" +#include "qqmlscript_p.h" +#include "qqmlbinding_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qxmlstream.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlCompiler; + +class QQmlCustomParserPropertyPrivate; +class Q_QML_EXPORT QQmlCustomParserProperty +{ +public: + QQmlCustomParserProperty(); + QQmlCustomParserProperty(const QQmlCustomParserProperty &); + QQmlCustomParserProperty &operator=(const QQmlCustomParserProperty &); + ~QQmlCustomParserProperty(); + + QString name() const; + QQmlScript::Location location() const; + + bool isList() const; + // Will be one of QQmlScript::Variant, QQmlCustomParserProperty or + // QQmlCustomParserNode + QList<QVariant> assignedValues() const; + +private: + friend class QQmlCustomParserNodePrivate; + friend class QQmlCustomParserPropertyPrivate; + QQmlCustomParserPropertyPrivate *d; +}; + +class QQmlCustomParserNodePrivate; +class Q_QML_EXPORT QQmlCustomParserNode +{ +public: + QQmlCustomParserNode(); + QQmlCustomParserNode(const QQmlCustomParserNode &); + QQmlCustomParserNode &operator=(const QQmlCustomParserNode &); + ~QQmlCustomParserNode(); + + QString name() const; + QQmlScript::Location location() const; + + QList<QQmlCustomParserProperty> properties() const; + +private: + friend class QQmlCustomParserNodePrivate; + QQmlCustomParserNodePrivate *d; +}; + +class Q_QML_EXPORT QQmlCustomParser +{ +public: + enum Flag { + NoFlag = 0x00000000, + AcceptsAttachedProperties = 0x00000001, + AcceptsSignalHandlers = 0x00000002 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QQmlCustomParser() : compiler(0), object(0), m_flags(NoFlag) {} + QQmlCustomParser(Flags f) : compiler(0), object(0), m_flags(f) {} + virtual ~QQmlCustomParser() {} + + void clearErrors(); + Flags flags() const { return m_flags; } + + virtual QByteArray compile(const QList<QQmlCustomParserProperty> &)=0; + virtual void setCustomData(QObject *, const QByteArray &)=0; + + QList<QQmlError> errors() const { return exceptions; } + +protected: + void error(const QString& description); + void error(const QQmlCustomParserProperty&, const QString& description); + void error(const QQmlCustomParserNode&, const QString& description); + + int evaluateEnum(const QByteArray&) const; + + const QMetaObject *resolveType(const QString&) const; + + QQmlBinding::Identifier rewriteBinding(const QQmlScript::Variant&, const QString&); + QString rewriteSignalHandler(const QQmlScript::Variant&, const QString&); + +private: + QList<QQmlError> exceptions; + QQmlCompiler *compiler; + QQmlScript::Object *object; + Flags m_flags; + friend class QQmlCompiler; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlCustomParser::Flags); + +#if 0 +#define QML_REGISTER_CUSTOM_TYPE(URI, VERSION_MAJ, VERSION_MIN, NAME, TYPE, CUSTOMTYPE) \ + qmlRegisterCustomType<TYPE>(#URI, VERSION_MAJ, VERSION_MIN, #NAME, #TYPE, new CUSTOMTYPE) +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlCustomParserProperty) +Q_DECLARE_METATYPE(QQmlCustomParserNode) + +QT_END_HEADER + +#endif diff --git a/src/qml/qml/qqmlcustomparser_p_p.h b/src/qml/qml/qqmlcustomparser_p_p.h new file mode 100644 index 0000000000..c861f9e944 --- /dev/null +++ b/src/qml/qml/qqmlcustomparser_p_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCUSTOMPARSER_P_H +#define QQMLCUSTOMPARSER_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 "qqmlcustomparser_p.h" + +#include "qqmlscript_p.h" + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QQmlCustomParserNodePrivate +{ +public: + QString name; + QList<QQmlCustomParserProperty> properties; + QQmlScript::Location location; + + static QQmlCustomParserNode fromObject(QQmlScript::Object *); + static QQmlCustomParserProperty fromProperty(QQmlScript::Property *); +}; + +class QQmlCustomParserPropertyPrivate +{ +public: + QQmlCustomParserPropertyPrivate() + : isList(false) {} + + QString name; + bool isList; + QQmlScript::Location location; + QList<QVariant> values; +}; + +QT_END_NAMESPACE + +#endif // QQMLCUSTOMPARSER_P_H diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h new file mode 100644 index 0000000000..e4ba44583d --- /dev/null +++ b/src/qml/qml/qqmldata_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATA_P_H +#define QQMLDATA_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/qobject_p.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +template <class Key, class T> class QHash; +class QQmlGuardImpl; +class QQmlCompiledData; +class QQmlAbstractBinding; +class QQmlContext; +class QQmlPropertyCache; +class QQmlContextData; +class QQmlNotifier; +class QQmlDataExtended; +class QQmlNotifierEndpoint; +// This class is structured in such a way, that simply zero'ing it is the +// default state for elemental object allocations. This is crucial in the +// workings of the QQmlInstruction::CreateSimpleObject instruction. +// Don't change anything here without first considering that case! +class Q_QML_EXPORT QQmlData : public QAbstractDeclarativeData +{ +public: + QQmlData() + : ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + hasTaintedV8Object(false), notifyList(0), context(0), outerContext(0), bindings(0), + nextContextObject(0), prevContextObject(0), bindingBitsSize(0), bindingBits(0), + lineNumber(0), columnNumber(0), deferredComponent(0), deferredIdx(0), v8objectid(0), + propertyCache(0), guards(0), extendedData(0) { + init(); + } + + static inline void init() { + QAbstractDeclarativeData::destroyed = destroyed; + QAbstractDeclarativeData::parentChanged = parentChanged; + QAbstractDeclarativeData::objectNameChanged = objectNameChanged; + QAbstractDeclarativeData::signalEmitted = signalEmitted; + } + + static void destroyed(QAbstractDeclarativeData *, QObject *); + static void parentChanged(QAbstractDeclarativeData *, QObject *, QObject *); + static void objectNameChanged(QAbstractDeclarativeData *, QObject *); + static void signalEmitted(QAbstractDeclarativeData *, QObject *, int, void **); + + void destroyed(QObject *); + void parentChanged(QObject *, QObject *); + void objectNameChanged(QObject *); + + void setImplicitDestructible() { + if (!explicitIndestructibleSet) indestructible = false; + } + + quint32 ownMemory:1; + quint32 ownContext:1; + quint32 indestructible:1; + quint32 explicitIndestructibleSet:1; + quint32 hasTaintedV8Object:1; + quint32 dummy:27; + + struct NotifyList { + quint64 connectionMask; + + quint16 maximumTodoIndex; + quint16 notifiesSize; + + QQmlNotifierEndpoint *todo; + QQmlNotifierEndpoint**notifies; + void layout(); + private: + void layout(QQmlNotifierEndpoint*); + }; + NotifyList *notifyList; + + inline QQmlNotifierEndpoint *notify(int index); + void addNotify(int index, QQmlNotifierEndpoint *); + + // The context that created the C++ object + QQmlContextData *context; + // The outermost context in which this object lives + QQmlContextData *outerContext; + + QQmlAbstractBinding *bindings; + + // Linked list for QQmlContext::contextObjects + QQmlData *nextContextObject; + QQmlData**prevContextObject; + + int bindingBitsSize; + quint32 *bindingBits; + bool hasBindingBit(int) const; + void clearBindingBit(int); + void setBindingBit(QObject *obj, int); + + ushort lineNumber; + ushort columnNumber; + + QQmlCompiledData *deferredComponent; // Can't this be found from the context? + unsigned int deferredIdx; + + quint32 v8objectid; + v8::Persistent<v8::Object> v8object; + + QQmlPropertyCache *propertyCache; + + QQmlGuardImpl *guards; + + static QQmlData *get(const QObject *object, bool create = false) { + QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); + if (priv->wasDeleted) { + Q_ASSERT(!create); + return 0; + } else if (priv->declarativeData) { + return static_cast<QQmlData *>(priv->declarativeData); + } else if (create) { + priv->declarativeData = new QQmlData; + return static_cast<QQmlData *>(priv->declarativeData); + } else { + return 0; + } + } + + bool hasExtendedData() const { return extendedData != 0; } + QQmlNotifier *objectNameNotifier() const; + QHash<int, QObject *> *attachedProperties() const; + +private: + // For objectNameNotifier and attachedProperties + mutable QQmlDataExtended *extendedData; +}; + +QQmlNotifierEndpoint *QQmlData::notify(int index) +{ + Q_ASSERT(index <= 0xFFFF); + + if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) { + return 0; + } else if (index < notifyList->notifiesSize) { + return notifyList->notifies[index]; + } else if (index <= notifyList->maximumTodoIndex) { + notifyList->layout(); + } + + if (index < notifyList->notifiesSize) { + return notifyList->notifies[index]; + } else { + return 0; + } +} + +QT_END_NAMESPACE + +#endif // QQMLDATA_P_H diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp new file mode 100644 index 0000000000..7b99214f04 --- /dev/null +++ b/src/qml/qml/qqmldirparser.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldirparser_p.h" +#include "qqmlerror.h" +#include "qqmlglobal_p.h" + +#include <QtCore/QTextStream> +#include <QtCore/QFile> +#include <QtCore/QtDebug> + +QT_BEGIN_NAMESPACE + +QQmlDirParser::QQmlDirParser() + : _isParsed(false) +{ +} + +QQmlDirParser::~QQmlDirParser() +{ +} + +QUrl QQmlDirParser::url() const +{ + return _url; +} + +void QQmlDirParser::setUrl(const QUrl &url) +{ + _url = url; +} + +QString QQmlDirParser::fileSource() const +{ + return _filePathSouce; +} + +void QQmlDirParser::setFileSource(const QString &filePath) +{ + _filePathSouce = filePath; +} + +QString QQmlDirParser::source() const +{ + return _source; +} + +void QQmlDirParser::setSource(const QString &source) +{ + _isParsed = false; + _source = source; +} + +bool QQmlDirParser::isParsed() const +{ + return _isParsed; +} + +bool QQmlDirParser::parse() +{ + if (_isParsed) + return true; + + _isParsed = true; + _errors.clear(); + _plugins.clear(); + _components.clear(); + _scripts.clear(); + + if (_source.isEmpty() && !_filePathSouce.isEmpty()) { + QFile file(_filePathSouce); + if (!QQml_isFileCaseCorrect(_filePathSouce)) { + QQmlError error; + error.setDescription(QString::fromUtf8("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"").arg(_filePathSouce)); + _errors.prepend(error); + return false; + } else if (file.open(QFile::ReadOnly)) { + _source = QString::fromUtf8(file.readAll()); + } else { + QQmlError error; + error.setDescription(QString::fromUtf8("module \"$$URI$$\" definition \"%1\" not readable").arg(_filePathSouce)); + _errors.prepend(error); + return false; + } + } + + QTextStream stream(&_source); + int lineNumber = 0; + + forever { + ++lineNumber; + + const QString line = stream.readLine(); + if (line.isNull()) + break; + + QString sections[3]; + int sectionCount = 0; + + int index = 0; + const int length = line.length(); + + while (index != length) { + const QChar ch = line.at(index); + + if (ch.isSpace()) { + do { ++index; } + while (index != length && line.at(index).isSpace()); + + } else if (ch == QLatin1Char('#')) { + // recognized a comment + break; + + } else { + const int start = index; + + do { ++index; } + while (index != length && !line.at(index).isSpace()); + + const QString lexeme = line.mid(start, index - start); + + if (sectionCount >= 3) { + reportError(lineNumber, start, QLatin1String("unexpected token")); + + } else { + sections[sectionCount++] = lexeme; + } + } + } + + if (sectionCount == 0) { + continue; // no sections, no party. + + } else if (sections[0] == QLatin1String("plugin")) { + if (sectionCount < 2) { + reportError(lineNumber, -1, + QString::fromUtf8("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); + + continue; + } + + const Plugin entry(sections[1], sections[2]); + + _plugins.append(entry); + + } else if (sections[0] == QLatin1String("internal")) { + if (sectionCount != 3) { + reportError(lineNumber, -1, + QString::fromUtf8("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); + continue; + } + Component entry(sections[1], sections[2], -1, -1); + entry.internal = true; + _components.append(entry); + } else if (sections[0] == QLatin1String("typeinfo")) { + if (sectionCount != 2) { + reportError(lineNumber, -1, + QString::fromUtf8("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); + continue; + } +#ifdef QT_CREATOR + TypeInfo typeInfo(sections[1]); + _typeInfos.append(typeInfo); +#endif + + } else if (sectionCount == 2) { + // No version specified (should only be used for relative qmldir files) + const Component entry(sections[0], sections[1], -1, -1); + _components.append(entry); + } else if (sectionCount == 3) { + const QString &version = sections[1]; + const int dotIndex = version.indexOf(QLatin1Char('.')); + + if (dotIndex == -1) { + reportError(lineNumber, -1, QLatin1String("expected '.'")); + } else if (version.indexOf(QLatin1Char('.'), dotIndex + 1) != -1) { + reportError(lineNumber, -1, QLatin1String("unexpected '.'")); + } else { + bool validVersionNumber = false; + const int majorVersion = version.left(dotIndex).toInt(&validVersionNumber); + + if (validVersionNumber) { + const int minorVersion = version.mid(dotIndex + 1).toInt(&validVersionNumber); + + if (validVersionNumber) { + const QString &fileName = sections[2]; + + if (fileName.endsWith(QLatin1String(".js"))) { + // A 'js' extension indicates a namespaced script import + const Script entry(sections[0], fileName, majorVersion, minorVersion); + _scripts.append(entry); + } else { + const Component entry(sections[0], fileName, majorVersion, minorVersion); + _components.append(entry); + } + } + } + } + } else { + reportError(lineNumber, -1, + QString::fromUtf8("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); + } + } + + return hasError(); +} + +void QQmlDirParser::reportError(int line, int column, const QString &description) +{ + QQmlError error; + error.setUrl(_url); + error.setLine(line); + error.setColumn(column); + error.setDescription(description); + _errors.append(error); +} + +bool QQmlDirParser::hasError() const +{ + if (! _errors.isEmpty()) + return true; + + return false; +} + +QList<QQmlError> QQmlDirParser::errors(const QString &uri) const +{ + QList<QQmlError> errors = _errors; + for (int i = 0; i < errors.size(); ++i) { + QQmlError &e = errors[i]; + QString description = e.description(); + description.replace(QLatin1String("$$URI$$"), uri); + e.setDescription(description); + } + return errors; +} + +QList<QQmlDirParser::Plugin> QQmlDirParser::plugins() const +{ + return _plugins; +} + +QList<QQmlDirParser::Component> QQmlDirParser::components() const +{ + return _components; +} + +QList<QQmlDirParser::Script> QQmlDirParser::scripts() const +{ + return _scripts; +} + +#ifdef QT_CREATOR +QList<QQmlDirParser::TypeInfo> QQmlDirParser::typeInfos() const +{ + return _typeInfos; +} +#endif + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h new file mode 100644 index 0000000000..8c681309ac --- /dev/null +++ b/src/qml/qml/qqmldirparser_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDIRPARSER_P_H +#define QQMLDIRPARSER_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/QUrl> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QQmlError; +class QQmlDirParser +{ + Q_DISABLE_COPY(QQmlDirParser) + +public: + QQmlDirParser(); + ~QQmlDirParser(); + + QUrl url() const; + void setUrl(const QUrl &url); + + QString fileSource() const; + void setFileSource(const QString &filePath); + + QString source() const; + void setSource(const QString &source); + + bool isParsed() const; + bool parse(); + + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + struct Plugin + { + Plugin() {} + + Plugin(const QString &name, const QString &path) + : name(name), path(path) {} + + QString name; + QString path; + }; + + struct Component + { + Component() + : majorVersion(0), minorVersion(0), internal(false) {} + + Component(const QString &typeName, const QString &fileName, int majorVersion, int minorVersion) + : typeName(typeName), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion), + internal(false) {} + + QString typeName; + QString fileName; + int majorVersion; + int minorVersion; + bool internal; + }; + + struct Script + { + Script() + : majorVersion(0), minorVersion(0) {} + + Script(const QString &nameSpace, const QString &fileName, int majorVersion, int minorVersion) + : nameSpace(nameSpace), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion) {} + + QString nameSpace; + QString fileName; + int majorVersion; + int minorVersion; + }; + + QList<Component> components() const; + QList<Script> scripts() const; + QList<Plugin> plugins() const; + +#ifdef QT_CREATOR + struct TypeInfo + { + TypeInfo() {} + TypeInfo(const QString &fileName) + : fileName(fileName) {} + + QString fileName; + }; + + QList<TypeInfo> typeInfos() const; +#endif + +private: + void reportError(int line, int column, const QString &message); + +private: + QList<QQmlError> _errors; + QUrl _url; + QString _source; + QString _filePathSouce; + QList<Component> _components; + QList<Script> _scripts; + QList<Plugin> _plugins; +#ifdef QT_CREATOR + QList<TypeInfo> _typeInfos; +#endif + unsigned _isParsed: 1; +}; + +typedef QList<QQmlDirParser::Component> QQmlDirComponents; +typedef QList<QQmlDirParser::Script> QQmlDirScripts; + + +QT_END_NAMESPACE + +#endif // QQMLDIRPARSER_P_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp new file mode 100644 index 0000000000..8cfe635543 --- /dev/null +++ b/src/qml/qml/qqmlengine.cpp @@ -0,0 +1,1854 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlengine_p.h" +#include "qqmlengine.h" +#include "qqmlcomponentattached_p.h" + +#include "qqmlcontext_p.h" +#include "qqmlcompiler_p.h" +#include "qqml.h" +#include "qqmlcontext.h" +#include "qqmlexpression.h" +#include "qqmlcomponent.h" +#include "qqmlbinding_p_p.h" +#include "qqmlvme_p.h" +#include <private/qqmlenginedebugservice_p.h> +#include "qqmlstringconverters_p.h" +#include "qqmlxmlhttprequest_p.h" +#include "qqmlscriptstring.h" +#include "qqmlglobal_p.h" +#include "qquicklistmodel_p.h" +#include "qquickworkerscript_p.h" +#include "qqmlcomponent_p.h" +#include "qqmlnetworkaccessmanagerfactory.h" +#include "qqmlimageprovider.h" +#include "qqmldirparser_p.h" +#include "qqmlextensioninterface.h" +#include "qqmllist_p.h" +#include "qqmltypenamecache_p.h" +#include "qqmlnotifier_p.h" +#include <private/qqmlprofilerservice_p.h> +#include <private/qquickapplication_p.h> +#include <private/qv8debugservice_p.h> +#include <private/qdebugmessageservice_p.h> +#include "qqmlincubator.h" +#include <private/qv8profilerservice_p.h> + +#include <QtCore/qstandardpaths.h> +#include <QtCore/qsettings.h> + +#include <QtCore/qmetaobject.h> +#include <QNetworkAccessManager> +#include <QDebug> +#include <QMetaObject> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <QtCore/qmutex.h> +#include <QtNetwork/qnetworkconfigmanager.h> + +#include <private/qobject_p.h> + +#include <private/qqmllocale_p.h> + +#ifdef Q_OS_WIN // for %APPDATA% +#include <qt_windows.h> +#include <qlibrary.h> +#include <windows.h> + +#define CSIDL_APPDATA 0x001a // <username>\Application Data +#endif + +Q_DECLARE_METATYPE(QQmlProperty) + +QT_BEGIN_NAMESPACE + +void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) +{ + QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); + QQmlValueTypeFactory::registerBaseTypes(uri, versionMajor, versionMinor); +} + +/*! + \qmlclass QtObject QObject + \ingroup qml-utility-elements + \since 4.7 + \brief The QtObject element is the most basic element in QML. + + The QtObject element is a non-visual element which contains only the + objectName property. + + It can be useful to create a QtObject if you need an extremely + lightweight element to enclose a set of custom properties: + + \snippet doc/src/snippets/qml/qtobject.qml 0 + + It can also be useful for C++ integration, as it is just a plain + QObject. See the QObject documentation for further details. +*/ +/*! + \qmlproperty string QtObject::objectName + This property holds the QObject::objectName for this specific object instance. + + This allows a C++ application to locate an item within a QML component + using the QObject::findChild() method. For example, the following C++ + application locates the child \l Rectangle item and dynamically changes its + \c color value: + + \qml + // MyRect.qml + + import QtQuick 2.0 + + Item { + width: 200; height: 200 + + Rectangle { + anchors.fill: parent + color: "red" + objectName: "myRect" + } + } + \endqml + + \code + // main.cpp + + QQuickView view; + view.setSource(QUrl::fromLocalFile("MyRect.qml")); + view.show(); + + QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("myRect"); + if (item) + item->setProperty("color", QColor(Qt::yellow)); + \endcode +*/ + +bool QQmlEnginePrivate::qml_debugging_enabled = false; + +void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int versionMinor) +{ + qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component"); + qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject"); + qmlRegisterType<QQuickListElement>(uri, versionMajor, versionMinor,"ListElement"); + qmlRegisterCustomType<QQuickListModel>(uri, versionMajor, versionMinor,"ListModel", new QQuickListModelParser); + qmlRegisterType<QQuickWorkerScript>(uri,versionMajor,versionMinor,"WorkerScript"); +} + +void QQmlEnginePrivate::defineModule() +{ + registerBaseTypes("QtQuick", 2, 0); + qmlRegisterType<QQmlBinding>(); + qmlRegisterUncreatableType<QQuickApplication>("QtQuick",2,0,"Application", QQuickApplication::tr("Application is an abstract class")); + qmlRegisterUncreatableType<QQmlLocale>("QtQuick",2,0,"Locale",QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); +} + +/*! +\qmlclass Qt QQmlEnginePrivate + \ingroup qml-utility-elements +\brief The QML global Qt object provides useful enums and functions from Qt. + +\keyword QmlGlobalQtObject + +\brief The \c Qt object provides useful enums and functions from Qt, for use in all QML files. + +The \c Qt object is a global object with utility functions, properties and enums. + +It is not instantiable; to use it, call the members of the global \c Qt object directly. +For example: + +\qml +import QtQuick 2.0 + +Text { + color: Qt.rgba(1, 0, 0, 1) + text: Qt.md5("hello, world") +} +\endqml + + +\section1 Enums + +The Qt object contains the enums available in the \l {Qt Namespace}. For example, you can access +the \l Qt::LeftButton and \l Qt::RightButton enum values as \c Qt.LeftButton and \c Qt.RightButton. + + +\section1 Types +The Qt object also contains helper functions for creating objects of specific +data types. This is primarily useful when setting the properties of an item +when the property has one of the following types: + +\list +\o \c color - use \l{Qt::rgba()}{Qt.rgba()}, \l{Qt::hsla()}{Qt.hsla()}, \l{Qt::darker()}{Qt.darker()}, \l{Qt::lighter()}{Qt.lighter()} or \l{Qt::tint()}{Qt.tint()} +\o \c rect - use \l{Qt::rect()}{Qt.rect()} +\o \c point - use \l{Qt::point()}{Qt.point()} +\o \c size - use \l{Qt::size()}{Qt.size()} +\o \c vector3d - use \l{Qt::vector3d()}{Qt.vector3d()} +\endlist + +There are also string based constructors for these types. See \l{qdeclarativebasictypes.html}{QML Basic Types} for more information. + +\section1 Date/Time Formatters + +The Qt object contains several functions for formatting QDateTime, QDate and QTime values. + +\list + \o \l{Qt::formatDateTime}{string Qt.formatDateTime(datetime date, variant format)} + \o \l{Qt::formatDate}{string Qt.formatDate(datetime date, variant format)} + \o \l{Qt::formatTime}{string Qt.formatTime(datetime date, variant format)} +\endlist + +The format specification is described at \l{Qt::formatDateTime}{Qt.formatDateTime}. + + +\section1 Dynamic Object Creation +The following functions on the global object allow you to dynamically create QML +items from files or strings. See \l{Dynamic Object Management in QML} for an overview +of their use. + +\list + \o \l{Qt::createComponent()}{object Qt.createComponent(url)} + \o \l{Qt::createQmlObject()}{object Qt.createQmlObject(string qml, object parent, string filepath)} +\endlist +*/ + + +/*! + \qmlproperty object Qt::application + \since QtQuick 1.1 + + The \c application object provides access to global application state + properties shared by many QML components. + + Its properties are: + + \table + \row + \o \c application.active + \o + This read-only property indicates whether the application is the top-most and focused + application, and the user is able to interact with the application. The property + is false when the application is in the background, the device keylock or screen + saver is active, the screen backlight is turned off, or the global system dialog + is being displayed on top of the application. It can be used for stopping and + pausing animations, timers and active processing of data in order to save device + battery power and free device memory and processor load when the application is not + active. + + \row + \o \c application.layoutDirection + \o + This read-only property can be used to query the default layout direction of the + application. On system start-up, the default layout direction depends on the + application's language. The property has a value of \c Qt.RightToLeft in locales + where text and graphic elements are read from right to left, and \c Qt.LeftToRight + where the reading direction flows from left to right. You can bind to this + property to customize your application layouts to support both layout directions. + + Possible values are: + + \list + \o Qt.LeftToRight - Text and graphics elements should be positioned + from left to right. + \o Qt.RightToLeft - Text and graphics elements should be positioned + from right to left. + \endlist + + \row + \o \c application.inputPanel + \o + This read-only property allows access to application's QInputPanel object + and all its properties and slots. See the QInputPanel documentation for + further details. Deprecated in favor of Qt.InputMethod + \endtable + + The following example uses the \c application object to indicate + whether the application is currently active: + + \snippet doc/src/snippets/qml/application.qml document + + \qmlproperty object Qt::inputMethod + \since QtQuick 2.0 + + The \c inputMethod object allows access to application's QInputMethod object + and all its properties and slots. See the QInputMethod documentation for + further details. +*/ + + +/*! +\qmlmethod object Qt::include(string url, jsobject callback) + +Includes another JavaScript file. This method can only be used from within JavaScript files, +and not regular QML files. + +This imports all functions from \a url into the current script's namespace. + +Qt.include() returns an object that describes the status of the operation. The object has +a single property, \c {status}, that is set to one of the following values: + +\table +\header \o Symbol \o Value \o Description +\row \o result.OK \o 0 \o The include completed successfully. +\row \o result.LOADING \o 1 \o Data is being loaded from the network. +\row \o result.NETWORK_ERROR \o 2 \o A network error occurred while fetching the url. +\row \o result.EXCEPTION \o 3 \o A JavaScript exception occurred while executing the included code. +An additional \c exception property will be set in this case. +\endtable + +The \c status property will be updated as the operation progresses. + +If provided, \a callback is invoked when the operation completes. The callback is passed +the same object as is returned from the Qt.include() call. +*/ +// Qt.include() is implemented in qv8include.cpp + + +QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) +: propertyCapture(0), rootContext(0), isDebugging(false), + outputWarningsToStdErr(true), sharedContext(0), sharedScope(0), + cleanup(0), erroredBindings(0), inProgressCreations(0), + workerScriptEngine(0), activeVME(0), + networkAccessManager(0), networkAccessManagerFactory(0), + scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), + incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) +{ +} + +QQmlEnginePrivate::~QQmlEnginePrivate() +{ + Q_ASSERT(inProgressCreations == 0); + + while (cleanup) { + QQmlCleanup *c = cleanup; + cleanup = c->next; + if (cleanup) cleanup->prev = &cleanup; + c->next = 0; + c->prev = 0; + c->clear(); + } + + doDeleteInEngineThread(); + + if (incubationController) incubationController->d = 0; + incubationController = 0; + + delete rootContext; + rootContext = 0; + + for(QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constBegin(); iter != m_compositeTypes.constEnd(); ++iter) + (*iter)->release(); + for(QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter) + (*iter)->release(); + for(QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::Iterator iter = typePropertyCache.begin(); iter != typePropertyCache.end(); ++iter) + (*iter)->release(); + for(QHash<QQmlMetaType::ModuleApi, QQmlMetaType::ModuleApiInstance *>::Iterator iter = moduleApiInstances.begin(); iter != moduleApiInstances.end(); ++iter) { + delete (*iter)->qobjectApi; + delete *iter; + } +} + +void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) +{ + QObjectPrivate *p = QObjectPrivate::get(o); + if (p->declarativeData) { + QQmlData *d = static_cast<QQmlData*>(p->declarativeData); + if (d->ownContext && d->context) { + d->context->destroy(); + d->context = 0; + } + } +} + +void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o) +{ + static_cast<QQmlData *>(d)->destroyed(o); +} + +void QQmlData::parentChanged(QAbstractDeclarativeData *d, QObject *o, QObject *p) +{ + static_cast<QQmlData *>(d)->parentChanged(o, p); +} + +void QQmlData::objectNameChanged(QAbstractDeclarativeData *d, QObject *o) +{ + static_cast<QQmlData *>(d)->objectNameChanged(o); +} + +void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int index, void **) +{ + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata) return; // Probably being deleted + + QQmlNotifierEndpoint *ep = ddata->notify(index); + if (ep) QQmlNotifier::emitNotify(ep); +} + +void QQmlEnginePrivate::init() +{ + Q_Q(QQmlEngine); + + static bool firstTime = true; + if (firstTime) { + qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); + + firstTime = false; + } + + qRegisterMetaType<QVariant>("QVariant"); + qRegisterMetaType<QQmlScriptString>("QQmlScriptString"); + qRegisterMetaType<QJSValue>("QJSValue"); + qRegisterMetaType<QQmlComponent::Status>("QQmlComponent::Status"); + qRegisterMetaType<QList<QObject*> >("QList<QObject*>"); + qRegisterMetaType<QList<int> >("QList<int>"); + qRegisterMetaType<QQmlV8Handle>("QQmlV8Handle"); + + QQmlData::init(); + + v8engine()->setEngine(q); + + rootContext = new QQmlContext(q,true); + + if (QCoreApplication::instance()->thread() == q->thread() && + QQmlEngineDebugService::isDebuggingEnabled()) { + isDebugging = true; + QQmlEngineDebugService::instance()->addEngine(q); + QV8DebugService::initialize(v8engine()); + QV8ProfilerService::initialize(); + QQmlProfilerService::initialize(); + QDebugMessageService::instance(); + } + + QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator()) + + QDir::separator() + QLatin1String("QML") + + QDir::separator() + QLatin1String("OfflineStorage"); +} + +QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() +{ + Q_Q(QQmlEngine); + if (!workerScriptEngine) + workerScriptEngine = new QQuickWorkerScriptEngine(q); + return workerScriptEngine; +} + +/*! + \class QQmlEngine + \since 4.7 + \brief The QQmlEngine class provides an environment for instantiating QML components. + \mainclass + + Each QML component is instantiated in a QQmlContext. + QQmlContext's are essential for passing data to QML + components. In QML, contexts are arranged hierarchically and this + hierarchy is managed by the QQmlEngine. + + Prior to creating any QML components, an application must have + created a QQmlEngine to gain access to a QML context. The + following example shows how to create a simple Text item. + + \code + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nText { text: \"Hello world!\" }", QUrl()); + QQuickItem *item = qobject_cast<QQuickItem *>(component.create()); + + //add item to view, etc + ... + \endcode + + In this case, the Text item will be created in the engine's + \l {QQmlEngine::rootContext()}{root context}. + + \sa QQmlComponent QQmlContext +*/ + +/*! + Create a new QQmlEngine with the given \a parent. +*/ +QQmlEngine::QQmlEngine(QObject *parent) +: QJSEngine(*new QQmlEnginePrivate(this), parent) +{ + Q_D(QQmlEngine); + d->init(); +} + +/*! + Destroys the QQmlEngine. + + Any QQmlContext's created on this engine will be + invalidated, but not destroyed (unless they are parented to the + QQmlEngine object). +*/ +QQmlEngine::~QQmlEngine() +{ + Q_D(QQmlEngine); + if (d->isDebugging) { + QQmlEngineDebugService::instance()->remEngine(this); + } + + // if we are the parent of any of the qobject module api instances, + // we need to remove them from our internal list, in order to prevent + // a segfault in engine private dtor. + QList<QQmlMetaType::ModuleApi> keys = d->moduleApiInstances.keys(); + QObject *currQObjectApi = 0; + QQmlMetaType::ModuleApiInstance *currInstance = 0; + foreach (const QQmlMetaType::ModuleApi &key, keys) { + currInstance = d->moduleApiInstances.value(key); + currQObjectApi = currInstance->qobjectApi; + if (this->children().contains(currQObjectApi)) { + delete currQObjectApi; + delete currInstance; + d->moduleApiInstances.remove(key); + } + } + + // ensure we clean up QObjects with JS ownership + d->v8engine()->gc(); + + if (d->incubationController) + d->incubationController->d = 0; +} + +/*! \fn void QQmlEngine::quit() + This signal is emitted when the QML loaded by the engine would like to quit. + */ + +/*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings) + This signal is emitted when \a warnings messages are generated by QML. + */ + +/*! + Clears the engine's internal component cache. + + Normally the QQmlEngine caches components loaded from qml + files. This method clears this cache and forces the component to be + reloaded. + */ +void QQmlEngine::clearComponentCache() +{ + Q_D(QQmlEngine); + d->typeLoader.clearCache(); +} + +/*! + Returns the engine's root context. + + The root context is automatically created by the QQmlEngine. + Data that should be available to all QML component instances + instantiated by the engine should be put in the root context. + + Additional data that should only be available to a subset of + component instances should be added to sub-contexts parented to the + root context. +*/ +QQmlContext *QQmlEngine::rootContext() const +{ + Q_D(const QQmlEngine); + return d->rootContext; +} + +/*! + Sets the \a factory to use for creating QNetworkAccessManager(s). + + QNetworkAccessManager is used for all network access by QML. By + implementing a factory it is possible to create custom + QNetworkAccessManager with specialized caching, proxy and cookie + support. + + The factory must be set before executing the engine. +*/ +void QQmlEngine::setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory) +{ + Q_D(QQmlEngine); + QMutexLocker locker(&d->mutex); + d->networkAccessManagerFactory = factory; +} + +/*! + Returns the current QQmlNetworkAccessManagerFactory. + + \sa setNetworkAccessManagerFactory() +*/ +QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const +{ + Q_D(const QQmlEngine); + return d->networkAccessManagerFactory; +} + +void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) +{ + if (activeVME) { + activeVME->finalizeCallbacks.append(qMakePair(QQmlGuard<QObject>(obj), index)); + } else { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); + } +} + +QNetworkAccessManager *QQmlEnginePrivate::createNetworkAccessManager(QObject *parent) const +{ + QMutexLocker locker(&mutex); + QNetworkAccessManager *nam; + if (networkAccessManagerFactory) { + nam = networkAccessManagerFactory->create(parent); + } else { + nam = new QNetworkAccessManager(parent); + } + + return nam; +} + +QNetworkAccessManager *QQmlEnginePrivate::getNetworkAccessManager() const +{ + Q_Q(const QQmlEngine); + if (!networkAccessManager) + networkAccessManager = createNetworkAccessManager(const_cast<QQmlEngine*>(q)); + return networkAccessManager; +} + +/*! + Returns a common QNetworkAccessManager which can be used by any QML + element instantiated by this engine. + + If a QQmlNetworkAccessManagerFactory has been set and a + QNetworkAccessManager has not yet been created, the + QQmlNetworkAccessManagerFactory will be used to create the + QNetworkAccessManager; otherwise the returned QNetworkAccessManager + will have no proxy or cache set. + + \sa setNetworkAccessManagerFactory() +*/ +QNetworkAccessManager *QQmlEngine::networkAccessManager() const +{ + Q_D(const QQmlEngine); + return d->getNetworkAccessManager(); +} + +/*! + + Sets the \a provider to use for images requested via the \e + image: url scheme, with host \a providerId. The QQmlEngine + takes ownership of \a provider. + + Image providers enable support for pixmap and threaded image + requests. See the QQmlImageProvider documentation for details on + implementing and using image providers. + + All required image providers should be added to the engine before any + QML sources files are loaded. + + \sa removeImageProvider() +*/ +void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProvider *provider) +{ + Q_D(QQmlEngine); + QMutexLocker locker(&d->mutex); + d->imageProviders.insert(providerId.toLower(), QSharedPointer<QQmlImageProvider>(provider)); +} + +/*! + Returns the QQmlImageProvider set for \a providerId. + + Returns the provider if it was found; otherwise returns 0. +*/ +QQmlImageProvider *QQmlEngine::imageProvider(const QString &providerId) const +{ + Q_D(const QQmlEngine); + QMutexLocker locker(&d->mutex); + return d->imageProviders.value(providerId).data(); +} + +/*! + Removes the QQmlImageProvider for \a providerId. + + \sa addImageProvider() +*/ +void QQmlEngine::removeImageProvider(const QString &providerId) +{ + Q_D(QQmlEngine); + QMutexLocker locker(&d->mutex); + d->imageProviders.take(providerId); +} + +QQmlImageProvider::ImageType QQmlEnginePrivate::getImageProviderType(const QUrl &url) +{ + QMutexLocker locker(&mutex); + QSharedPointer<QQmlImageProvider> provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) + return provider->imageType(); + return QQmlImageProvider::Invalid; +} + +QQuickTextureFactory *QQmlEnginePrivate::getTextureFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QSharedPointer<QQmlImageProvider> provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + return provider->requestTexture(imageId, size, req_size); + } + return 0; +} + +QImage QQmlEnginePrivate::getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QImage image; + QSharedPointer<QQmlImageProvider> provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + image = provider->requestImage(imageId, size, req_size); + } + return image; +} + +QPixmap QQmlEnginePrivate::getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QPixmap pixmap; + QSharedPointer<QQmlImageProvider> provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + pixmap = provider->requestPixmap(imageId, size, req_size); + } + return pixmap; +} + +/*! + Return the base URL for this engine. The base URL is only used to + resolve components when a relative URL is passed to the + QQmlComponent constructor. + + If a base URL has not been explicitly set, this method returns the + application's current working directory. + + \sa setBaseUrl() +*/ +QUrl QQmlEngine::baseUrl() const +{ + Q_D(const QQmlEngine); + if (d->baseUrl.isEmpty()) { + return QUrl::fromLocalFile(QDir::currentPath() + QDir::separator()); + } else { + return d->baseUrl; + } +} + +/*! + Set the base URL for this engine to \a url. + + \sa baseUrl() +*/ +void QQmlEngine::setBaseUrl(const QUrl &url) +{ + Q_D(QQmlEngine); + d->baseUrl = url; +} + +/*! + Returns true if warning messages will be output to stderr in addition + to being emitted by the warnings() signal, otherwise false. + + The default value is true. +*/ +bool QQmlEngine::outputWarningsToStandardError() const +{ + Q_D(const QQmlEngine); + return d->outputWarningsToStdErr; +} + +/*! + Set whether warning messages will be output to stderr to \a enabled. + + If \a enabled is true, any warning messages generated by QML will be + output to stderr and emitted by the warnings() signal. If \a enabled + is false, on the warnings() signal will be emitted. This allows + applications to handle warning output themselves. + + The default value is true. +*/ +void QQmlEngine::setOutputWarningsToStandardError(bool enabled) +{ + Q_D(QQmlEngine); + d->outputWarningsToStdErr = enabled; +} + +/*! + Attempt to free unused memory. +*/ +void QQmlEngine::collectGarbage() +{ + QV8Engine::gc(); +} + +/*! + Returns the QQmlContext for the \a object, or 0 if no + context has been set. + + When the QQmlEngine instantiates a QObject, the context is + set automatically. + */ +QQmlContext *QQmlEngine::contextForObject(const QObject *object) +{ + if(!object) + return 0; + + QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); + + QQmlData *data = + static_cast<QQmlData *>(priv->declarativeData); + + if (!data) + return 0; + else if (data->outerContext) + return data->outerContext->asQQmlContext(); + else + return 0; +} + +/*! + Sets the QQmlContext for the \a object to \a context. + If the \a object already has a context, a warning is + output, but the context is not changed. + + When the QQmlEngine instantiates a QObject, the context is + set automatically. + */ +void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context) +{ + if (!object || !context) + return; + + QQmlData *data = QQmlData::get(object, true); + if (data->context) { + qWarning("QQmlEngine::setContextForObject(): Object already has a QQmlContext"); + return; + } + + QQmlContextData *contextData = QQmlContextData::get(context); + contextData->addObject(object); +} + +/*! + \enum QQmlEngine::ObjectOwnership + + Ownership controls whether or not QML automatically destroys the + QObject when the object is garbage collected by the JavaScript + engine. The two ownership options are: + + \value CppOwnership The object is owned by C++ code, and will + never be deleted by QML. The JavaScript destroy() method cannot be + used on objects with CppOwnership. This option is similar to + QScriptEngine::QtOwnership. + + \value JavaScriptOwnership The object is owned by JavaScript. + When the object is returned to QML as the return value of a method + call or property access, QML will delete the object if there are no + remaining JavaScript references to it and it has no + QObject::parent(). This option is similar to + QScriptEngine::ScriptOwnership. + + Generally an application doesn't need to set an object's ownership + explicitly. QML uses a heuristic to set the default object + ownership. By default, an object that is created by QML has + JavaScriptOwnership. The exception to this are the root objects + created by calling QQmlComponent::create() or + QQmlComponent::beginCreate() which have CppOwnership by + default. The ownership of these root-level objects is considered to + have been transferred to the C++ caller. + + Objects not-created by QML have CppOwnership by default. The + exception to this is objects returned from a C++ method call. The + ownership of these objects is passed to JavaScript. + + Calling setObjectOwnership() overrides the default ownership + heuristic used by QML. +*/ + +/*! + Sets the \a ownership of \a object. +*/ +void QQmlEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership) +{ + if (!object) + return; + + QQmlData *ddata = QQmlData::get(object, true); + if (!ddata) + return; + + ddata->indestructible = (ownership == CppOwnership)?true:false; + ddata->explicitIndestructibleSet = true; +} + +/*! + Returns the ownership of \a object. +*/ +QQmlEngine::ObjectOwnership QQmlEngine::objectOwnership(QObject *object) +{ + if (!object) + return CppOwnership; + + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata) + return CppOwnership; + else + return ddata->indestructible?CppOwnership:JavaScriptOwnership; +} + +bool QQmlEngine::event(QEvent *e) +{ + Q_D(QQmlEngine); + if (e->type() == QEvent::User) + d->doDeleteInEngineThread(); + + return QJSEngine::event(e); +} + +void QQmlEnginePrivate::doDeleteInEngineThread() +{ + QFieldList<Deletable, &Deletable::next> list; + mutex.lock(); + list.copyAndClear(toDeleteInEngineThread); + mutex.unlock(); + + while (Deletable *d = list.takeFirst()) + delete d; +} + +Q_AUTOTEST_EXPORT void qmlExecuteDeferred(QObject *object) +{ + QQmlData *data = QQmlData::get(object); + + if (data && data->deferredComponent) { + if (QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService::startRange(QQmlProfilerService::Creating); + QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); + QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(object->metaObject()->className()); + QQmlProfilerService::rangeData(QQmlProfilerService::Creating, typeName); + if (data->outerContext) + QQmlProfilerService::rangeLocation(QQmlProfilerService::Creating, data->outerContext->url, data->lineNumber, data->columnNumber); + } + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + + QQmlComponentPrivate::ConstructionState state; + QQmlComponentPrivate::beginDeferred(ep, object, &state); + + data->deferredComponent->release(); + data->deferredComponent = 0; + + QQmlComponentPrivate::complete(ep, &state); + QQmlProfilerService::endRange(QQmlProfilerService::Creating); + } +} + +QQmlContext *qmlContext(const QObject *obj) +{ + return QQmlEngine::contextForObject(obj); +} + +QQmlEngine *qmlEngine(const QObject *obj) +{ + QQmlData *data = QQmlData::get(obj, false); + if (!data || !data->context) + return 0; + return data->context->engine; +} + +QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) +{ + QQmlData *data = QQmlData::get(object); + if (!data) + return 0; // Attached properties are only on objects created by QML + + QObject *rv = data->hasExtendedData()?data->attachedProperties()->value(id):0; + if (rv || !create) + return rv; + + QQmlAttachedPropertiesFunc pf = QQmlMetaType::attachedPropertiesFuncById(id); + if (!pf) + return 0; + + rv = pf(const_cast<QObject *>(object)); + + if (rv) + data->attachedProperties()->insert(id, rv); + + return rv; +} + +QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, + const QMetaObject *attachedMetaObject, bool create) +{ + if (*idCache == -1) + *idCache = QQmlMetaType::attachedPropertiesFuncId(attachedMetaObject); + + if (*idCache == -1 || !object) + return 0; + + return qmlAttachedPropertiesObjectById(*idCache, object, create); +} + +QQmlDebuggingEnabler::QQmlDebuggingEnabler() +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + if (!QQmlEnginePrivate::qml_debugging_enabled) { + qWarning("Qml debugging is enabled. Only use this in a safe environment!"); + } + QQmlEnginePrivate::qml_debugging_enabled = true; +#endif +} + + +class QQmlDataExtended { +public: + QQmlDataExtended(); + ~QQmlDataExtended(); + + QHash<int, QObject *> attachedProperties; + QQmlNotifier objectNameNotifier; +}; + +QQmlDataExtended::QQmlDataExtended() +{ +} + +QQmlDataExtended::~QQmlDataExtended() +{ +} + +void QQmlData::NotifyList::layout(QQmlNotifierEndpoint *endpoint) +{ + if (endpoint->next) + layout(endpoint->next); + + int index = endpoint->sourceSignal; + index = qMin(index, 0xFFFF - 1); + + endpoint->next = notifies[index]; + if (endpoint->next) endpoint->next->prev = &endpoint->next; + endpoint->prev = ¬ifies[index]; + notifies[index] = endpoint; +} + +void QQmlData::NotifyList::layout() +{ + Q_ASSERT(maximumTodoIndex >= notifiesSize); + + if (todo) { + QQmlNotifierEndpoint **old = notifies; + const int reallocSize = (maximumTodoIndex + 1) * sizeof(QQmlNotifierEndpoint*); + notifies = (QQmlNotifierEndpoint**)realloc(notifies, reallocSize); + const int memsetSize = (maximumTodoIndex - notifiesSize + 1) * + sizeof(QQmlNotifierEndpoint*); + memset(notifies + notifiesSize, 0, memsetSize); + + if (notifies != old) { + for (int ii = 0; ii < notifiesSize; ++ii) + if (notifies[ii]) + notifies[ii]->prev = ¬ifies[ii]; + } + + notifiesSize = maximumTodoIndex + 1; + + layout(todo); + } + + maximumTodoIndex = 0; + todo = 0; +} + +void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint) +{ + if (!notifyList) { + notifyList = (NotifyList *)malloc(sizeof(NotifyList)); + notifyList->connectionMask = 0; + notifyList->maximumTodoIndex = 0; + notifyList->notifiesSize = 0; + notifyList->todo = 0; + notifyList->notifies = 0; + } + + Q_ASSERT(!endpoint->isConnected()); + + index = qMin(index, 0xFFFF - 1); + notifyList->connectionMask |= (1ULL << quint64(index % 64)); + + if (index < notifyList->notifiesSize) { + + endpoint->next = notifyList->notifies[index]; + if (endpoint->next) endpoint->next->prev = &endpoint->next; + endpoint->prev = ¬ifyList->notifies[index]; + notifyList->notifies[index] = endpoint; + + } else { + notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index); + + endpoint->next = notifyList->todo; + if (endpoint->next) endpoint->next->prev = &endpoint->next; + endpoint->prev = ¬ifyList->todo; + notifyList->todo = endpoint; + } +} + +QQmlNotifier *QQmlData::objectNameNotifier() const +{ + if (!extendedData) extendedData = new QQmlDataExtended; + return &extendedData->objectNameNotifier; +} + +QHash<int, QObject *> *QQmlData::attachedProperties() const +{ + if (!extendedData) extendedData = new QQmlDataExtended; + return &extendedData->attachedProperties; +} + +void QQmlData::destroyed(QObject *object) +{ + if (deferredComponent) + deferredComponent->release(); + + if (nextContextObject) + nextContextObject->prevContextObject = prevContextObject; + if (prevContextObject) + *prevContextObject = nextContextObject; + + QQmlAbstractBinding *binding = bindings; + while (binding) { + QQmlAbstractBinding *next = binding->m_nextBinding; + binding->m_prevBinding = 0; + binding->m_nextBinding = 0; + binding->destroy(); + binding = next; + } + + if (bindingBits) + free(bindingBits); + + if (propertyCache) + propertyCache->release(); + + if (ownContext && context) + context->destroy(); + + while (guards) { + QQmlGuard<QObject> *guard = static_cast<QQmlGuard<QObject> *>(guards); + *guard = (QObject *)0; + guard->objectDestroyed(object); + } + + if (notifyList) { + while (notifyList->todo) + notifyList->todo->disconnect(); + for (int ii = 0; ii < notifyList->notifiesSize; ++ii) { + while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii]) + ep->disconnect(); + } + free(notifyList->notifies); + free(notifyList); + } + + if (extendedData) + delete extendedData; + + v8object.Clear(); // The WeakReference handler will clean the actual handle + + if (ownMemory) + delete this; +} + +void QQmlData::parentChanged(QObject *object, QObject *parent) +{ + Q_UNUSED(object); + Q_UNUSED(parent); +} + +void QQmlData::objectNameChanged(QObject *) +{ + if (extendedData) objectNameNotifier()->notify(); +} + +bool QQmlData::hasBindingBit(int bit) const +{ + if (bindingBitsSize > bit) + return bindingBits[bit / 32] & (1 << (bit % 32)); + else + return false; +} + +void QQmlData::clearBindingBit(int bit) +{ + if (bindingBitsSize > bit) + bindingBits[bit / 32] &= ~(1 << (bit % 32)); +} + +void QQmlData::setBindingBit(QObject *obj, int bit) +{ + if (bindingBitsSize <= bit) { + int props = obj->metaObject()->propertyCount(); + Q_ASSERT(bit < props); + + int arraySize = (props + 31) / 32; + int oldArraySize = bindingBitsSize / 32; + + bindingBits = (quint32 *)realloc(bindingBits, + arraySize * sizeof(quint32)); + + memset(bindingBits + oldArraySize, + 0x00, + sizeof(quint32) * (arraySize - oldArraySize)); + + bindingBitsSize = arraySize * 32; + } + + bindingBits[bit / 32] |= (1 << (bit % 32)); +} + +QString QQmlEnginePrivate::urlToLocalFileOrQrc(const QUrl& url) +{ + if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) { + if (url.authority().isEmpty()) + return QLatin1Char(':') + url.path(); + return QString(); + } + return url.toLocalFile(); +} + + +static QString toLocalFile(const QString &url) +{ + if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive)) + return QString(); + + QString file = url.mid(7); + + //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt" + + // magic for drives on windows + if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':')) + file.remove(0, 1); + + return file; +} + +QString QQmlEnginePrivate::urlToLocalFileOrQrc(const QString& url) +{ + if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) { + if (url.length() > 4) + return QLatin1Char(':') + url.mid(4); + return QString(); + } + + return toLocalFile(url); +} + +void QQmlEnginePrivate::sendQuit() +{ + Q_Q(QQmlEngine); + emit q->quit(); + if (q->receivers(SIGNAL(quit())) == 0) { + qWarning("Signal QQmlEngine::quit() emitted, but no receivers connected to handle it."); + } +} + +static void dumpwarning(const QQmlError &error) +{ + qWarning().nospace() << qPrintable(error.toString()); +} + +static void dumpwarning(const QList<QQmlError> &errors) +{ + for (int ii = 0; ii < errors.count(); ++ii) + dumpwarning(errors.at(ii)); +} + +void QQmlEnginePrivate::warning(const QQmlError &error) +{ + Q_Q(QQmlEngine); + q->warnings(QList<QQmlError>() << error); + if (outputWarningsToStdErr) + dumpwarning(error); +} + +void QQmlEnginePrivate::warning(const QList<QQmlError> &errors) +{ + Q_Q(QQmlEngine); + q->warnings(errors); + if (outputWarningsToStdErr) + dumpwarning(errors); +} + +void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error) +{ + if (engine) + QQmlEnginePrivate::get(engine)->warning(error); + else + dumpwarning(error); +} + +void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &error) +{ + if (engine) + QQmlEnginePrivate::get(engine)->warning(error); + else + dumpwarning(error); +} + +void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error) +{ + if (engine) + engine->warning(error); + else + dumpwarning(error); +} + +void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError> &error) +{ + if (engine) + engine->warning(error); + else + dumpwarning(error); +} + +/* + This function should be called prior to evaluation of any js expression, + so that scarce resources are not freed prematurely (eg, if there is a + nested javascript expression). + */ +void QQmlEnginePrivate::referenceScarceResources() +{ + scarceResourcesRefCount += 1; +} + +/* + This function should be called after evaluation of the js expression is + complete, and so the scarce resources may be freed safely. + */ +void QQmlEnginePrivate::dereferenceScarceResources() +{ + Q_ASSERT(scarceResourcesRefCount > 0); + scarceResourcesRefCount -= 1; + + // if the refcount is zero, then evaluation of the "top level" + // expression must have completed. We can safely release the + // scarce resources. + if (scarceResourcesRefCount == 0) { + // iterate through the list and release them all. + // note that the actual SRD is owned by the JS engine, + // so we cannot delete the SRD; but we can free the + // memory used by the variant in the SRD. + while (ScarceResourceData *sr = scarceResources.first()) { + sr->data = QVariant(); + scarceResources.remove(sr); + } + } +} + +/*! + Adds \a path as a directory where the engine searches for + installed modules in a URL-based directory structure. + The \a path may be a local filesystem directory or a URL. + + The newly added \a path will be first in the importPathList(). + + \sa setImportPathList(), {QML Modules} +*/ +void QQmlEngine::addImportPath(const QString& path) +{ + Q_D(QQmlEngine); + d->importDatabase.addImportPath(path); +} + +/*! + Returns the list of directories where the engine searches for + installed modules in a URL-based directory structure. + + For example, if \c /opt/MyApp/lib/imports is in the path, then QML that + imports \c com.mycompany.Feature will cause the QQmlEngine to look + in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components + provided by that module. A \c qmldir file is required for defining the + type version mapping and possibly declarative extensions plugins. + + By default, the list contains the directory of the application executable, + paths specified in the \c QML_IMPORT_PATH environment variable, + and the builtin \c ImportsPath from QLibraryInfo. + + \sa addImportPath() setImportPathList() +*/ +QStringList QQmlEngine::importPathList() const +{ + Q_D(const QQmlEngine); + return d->importDatabase.importPathList(); +} + +/*! + Sets \a paths as the list of directories where the engine searches for + installed modules in a URL-based directory structure. + + By default, the list contains the directory of the application executable, + paths specified in the \c QML_IMPORT_PATH environment variable, + and the builtin \c ImportsPath from QLibraryInfo. + + \sa importPathList() addImportPath() + */ +void QQmlEngine::setImportPathList(const QStringList &paths) +{ + Q_D(QQmlEngine); + d->importDatabase.setImportPathList(paths); +} + + +/*! + Adds \a path as a directory where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file). + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + The newly added \a path will be first in the pluginPathList(). + + \sa setPluginPathList() +*/ +void QQmlEngine::addPluginPath(const QString& path) +{ + Q_D(QQmlEngine); + d->importDatabase.addPluginPath(path); +} + + +/*! + Returns the list of directories where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file). + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + \sa addPluginPath() setPluginPathList() +*/ +QStringList QQmlEngine::pluginPathList() const +{ + Q_D(const QQmlEngine); + return d->importDatabase.pluginPathList(); +} + +/*! + Sets the list of directories where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file) + to \a paths. + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + \sa pluginPathList() addPluginPath() + */ +void QQmlEngine::setPluginPathList(const QStringList &paths) +{ + Q_D(QQmlEngine); + d->importDatabase.setPluginPathList(paths); +} + +/*! + Imports the plugin named \a filePath with the \a uri provided. + Returns true if the plugin was successfully imported; otherwise returns false. + + On failure and if non-null, the \a errors list will have any errors which occurred prepended to it. + + The plugin has to be a Qt plugin which implements the QQmlExtensionPlugin interface. +*/ +bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors) +{ + Q_D(QQmlEngine); + return d->importDatabase.importPlugin(filePath, uri, errors); +} + +/*! + Imports the plugin named \a filePath with the \a uri provided. + Returns true if the plugin was successfully imported; otherwise returns false. + + On failure and if non-null, *\a errorString will be set to a message describing the failure. + + The plugin has to be a Qt plugin which implements the QQmlExtensionPlugin interface. +*/ +bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QString *errorString) +{ + Q_D(QQmlEngine); + QList<QQmlError> errors; + bool retn = d->importDatabase.importPlugin(filePath, uri, &errors); + if (!errors.isEmpty()) { + QString builtError; + for (int i = 0; i < errors.size(); ++i) { + builtError = QString(QLatin1String("%1\n %2")) + .arg(builtError) + .arg(errors.at(i).toString()); + } + *errorString = builtError; + } + return retn; +} + +/*! + \property QQmlEngine::offlineStoragePath + \brief the directory for storing offline user data + + Returns the directory where SQL and other offline + storage is placed. + + QQuickWebView and the SQL databases created with openDatabase() + are stored here. + + The default is QML/OfflineStorage in the platform-standard + user application data directory. + + Note that the path may not currently exist on the filesystem, so + callers wanting to \e create new files at this location should create + it first - see QDir::mkpath(). +*/ +void QQmlEngine::setOfflineStoragePath(const QString& dir) +{ + Q_D(QQmlEngine); + d->offlineStoragePath = dir; +} + +QString QQmlEngine::offlineStoragePath() const +{ + Q_D(const QQmlEngine); + return d->offlineStoragePath; +} + +static void voidptr_destructor(void *v) +{ + void **ptr = (void **)v; + delete ptr; +} + +static void *voidptr_constructor(const void *v) +{ + if (!v) { + return new void*; + } else { + return new void*(*(void **)v); + } +} + +QQmlPropertyCache *QQmlEnginePrivate::createCache(const QMetaObject *mo) +{ + Q_Q(QQmlEngine); + + if (!mo->superClass()) { + QQmlPropertyCache *rv = new QQmlPropertyCache(q, mo); + propertyCache.insert(mo, rv); + return rv; + } else { + QQmlPropertyCache *super = cache(mo->superClass()); + QQmlPropertyCache *rv = super->copyAndAppend(q, mo); + propertyCache.insert(mo, rv); + return rv; + } +} + +QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion, + QQmlError &error) +{ + QList<QQmlType *> types; + + int maxMinorVersion = 0; + + const QMetaObject *metaObject = type->metaObject(); + + while (metaObject) { + QQmlType *t = QQmlMetaType::qmlType(metaObject, type->module(), + type->majorVersion(), minorVersion); + if (t) { + maxMinorVersion = qMax(maxMinorVersion, t->minorVersion()); + types << t; + } else { + types << 0; + } + + metaObject = metaObject->superClass(); + } + + if (QQmlPropertyCache *c = typePropertyCache.value(qMakePair(type, maxMinorVersion))) { + c->addref(); + typePropertyCache.insert(qMakePair(type, minorVersion), c); + return c; + } + + QQmlPropertyCache *raw = cache(type->metaObject()); + + bool hasCopied = false; + + for (int ii = 0; ii < types.count(); ++ii) { + QQmlType *currentType = types.at(ii); + if (!currentType) + 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 + + bool overloadError = false; + QString overloadName; + +#if 0 + 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; + } + } +#endif + + if (overloadError) { + if (hasCopied) raw->release(); + + error.setDescription(QLatin1String("Type ") + type->qmlTypeName() + QLatin1String(" ") + QString::number(type->majorVersion()) + QLatin1String(".") + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + return 0; + } + + if (!hasCopied) raw->addref(); + typePropertyCache.insert(qMakePair(type, minorVersion), raw); + + if (minorVersion != maxMinorVersion) { + raw->addref(); + typePropertyCache.insert(qMakePair(type, maxMinorVersion), raw); + } + + return raw; +} + +QQmlMetaType::ModuleApiInstance * +QQmlEnginePrivate::moduleApiInstance(const QQmlMetaType::ModuleApi &module) +{ + Locker locker(this); + + QQmlMetaType::ModuleApiInstance *a = moduleApiInstances.value(module); + if (!a) { + a = new QQmlMetaType::ModuleApiInstance; + a->scriptCallback = module.script; + a->qobjectCallback = module.qobject; + moduleApiInstances.insert(module, a); + } + + return a; +} + +bool QQmlEnginePrivate::isQObject(int t) +{ + Locker locker(this); + return m_compositeTypes.contains(t) || QQmlMetaType::isQObject(t); +} + +QObject *QQmlEnginePrivate::toQObject(const QVariant &v, bool *ok) const +{ + Locker locker(this); + int t = v.userType(); + if (t == QMetaType::QObjectStar || m_compositeTypes.contains(t)) { + if (ok) *ok = true; + return *(QObject **)(v.constData()); + } else { + return QQmlMetaType::toQObject(v, ok); + } +} + +QQmlMetaType::TypeCategory QQmlEnginePrivate::typeCategory(int t) const +{ + Locker locker(this); + if (m_compositeTypes.contains(t)) + return QQmlMetaType::Object; + else if (m_qmlLists.contains(t)) + return QQmlMetaType::List; + else + return QQmlMetaType::typeCategory(t); +} + +bool QQmlEnginePrivate::isList(int t) const +{ + Locker locker(this); + return m_qmlLists.contains(t) || QQmlMetaType::isList(t); +} + +int QQmlEnginePrivate::listType(int t) const +{ + Locker locker(this); + QHash<int, int>::ConstIterator iter = m_qmlLists.find(t); + if (iter != m_qmlLists.end()) + return *iter; + else + return QQmlMetaType::listType(t); +} + +const QMetaObject *QQmlEnginePrivate::rawMetaObjectForType(int t) const +{ + Locker locker(this); + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); + if (iter != m_compositeTypes.end()) { + return (*iter)->root; + } else { + QQmlType *type = QQmlMetaType::qmlType(t); + return type?type->baseMetaObject():0; + } +} + +const QMetaObject *QQmlEnginePrivate::metaObjectForType(int t) const +{ + Locker locker(this); + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); + if (iter != m_compositeTypes.end()) { + return (*iter)->root; + } else { + QQmlType *type = QQmlMetaType::qmlType(t); + return type?type->metaObject():0; + } +} + +void QQmlEnginePrivate::registerCompositeType(QQmlCompiledData *data) +{ + QByteArray name = data->root->className(); + + QByteArray ptr = name + '*'; + QByteArray lst = "QQmlListProperty<" + name + '>'; + + int ptr_type = QMetaType::registerType(ptr.constData(), voidptr_destructor, + voidptr_constructor); + int lst_type = QMetaType::registerType(lst.constData(), voidptr_destructor, + voidptr_constructor); + + data->addref(); + + Locker locker(this); + m_qmlLists.insert(lst_type, ptr_type); + m_compositeTypes.insert(ptr_type, data); +} + +bool QQml_isFileCaseCorrect(const QString &fileName) +{ +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) + QFileInfo info(fileName); + const QString absolute = info.absoluteFilePath(); + +#if defined(Q_OS_MAC) + const QString canonical = info.canonicalFilePath(); +#elif defined(Q_OS_WIN) + wchar_t buffer[1024]; + + DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); + if (rv == 0 || rv >= 1024) return true; + rv = ::GetLongPathName(buffer, buffer, 1024); + if (rv == 0 || rv >= 1024) return true; + + const QString canonical = QString::fromWCharArray(buffer); +#endif + + const int absoluteLength = absolute.length(); + const int canonicalLength = canonical.length(); + + const int length = qMin(absoluteLength, canonicalLength); + for (int ii = 0; ii < length; ++ii) { + const QChar &a = absolute.at(absoluteLength - 1 - ii); + const QChar &c = canonical.at(canonicalLength - 1 - ii); + + if (a.toLower() != c.toLower()) + return true; + if (a != c) + return false; + } +#else + Q_UNUSED(fileName) +#endif + return true; +} + +/*! + \fn QQmlEngine *qmlEngine(const QObject *object) + \relates QQmlEngine + + Returns the QQmlEngine associated with \a object, if any. This is equivalent to + QQmlEngine::contextForObject(object)->engine(), but more efficient. +*/ + +/*! + \fn QQmlContext *qmlContext(const QObject *object) + \relates QQmlEngine + + Returns the QQmlContext associated with \a object, if any. This is equivalent to + QQmlEngine::contextForObject(object). +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h new file mode 100644 index 0000000000..04ac61c05b --- /dev/null +++ b/src/qml/qml/qqmlengine.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENGINE_H +#define QQMLENGINE_H + +#include <QtCore/qurl.h> +#include <QtCore/qobject.h> +#include <QtCore/qmap.h> +#include <QtQml/qjsengine.h> +#include <QtQml/qqmlerror.h> +#include <QtQml/qqmldebug.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlComponent; +class QQmlEnginePrivate; +class QQmlImportsPrivate; +class QQmlExpression; +class QQmlContext; +class QQmlType; +class QUrl; +class QScriptContext; +class QQmlImageProvider; +class QNetworkAccessManager; +class QQmlNetworkAccessManagerFactory; +class QQmlIncubationController; +class Q_QML_EXPORT QQmlEngine : public QJSEngine +{ + Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) + Q_OBJECT +public: + QQmlEngine(QObject *p = 0); + virtual ~QQmlEngine(); + + QQmlContext *rootContext() const; + + void clearComponentCache(); + + QStringList importPathList() const; + void setImportPathList(const QStringList &paths); + void addImportPath(const QString& dir); + + QStringList pluginPathList() const; + void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& dir); + + bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); // XXX: Qt 5: Remove this function + bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); + + void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *); + QQmlNetworkAccessManagerFactory *networkAccessManagerFactory() const; + + QNetworkAccessManager *networkAccessManager() const; + + void addImageProvider(const QString &id, QQmlImageProvider *); + QQmlImageProvider *imageProvider(const QString &id) const; + void removeImageProvider(const QString &id); + + void setIncubationController(QQmlIncubationController *); + QQmlIncubationController *incubationController() const; + + void setOfflineStoragePath(const QString& dir); + QString offlineStoragePath() const; + + QUrl baseUrl() const; + void setBaseUrl(const QUrl &); + + bool outputWarningsToStandardError() const; + void setOutputWarningsToStandardError(bool); + + void collectGarbage(); + + static QQmlContext *contextForObject(const QObject *); + static void setContextForObject(QObject *, QQmlContext *); + + enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; + static void setObjectOwnership(QObject *, ObjectOwnership); + static ObjectOwnership objectOwnership(QObject *); + +protected: + virtual bool event(QEvent *); + +Q_SIGNALS: + void quit(); + void warnings(const QList<QQmlError> &warnings); + +private: + Q_DISABLE_COPY(QQmlEngine) + Q_DECLARE_PRIVATE(QQmlEngine) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLENGINE_H diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h new file mode 100644 index 0000000000..db834489ba --- /dev/null +++ b/src/qml/qml/qqmlengine_p.h @@ -0,0 +1,522 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLENGINE_P_H +#define QQMLENGINE_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 "qqmlengine.h" + +#include "qqmltypeloader_p.h" +#include "qqmlimport_p.h" +#include <private/qpodvector_p.h> +#include "qqml.h" +#include "qqmlvaluetype_p.h" +#include "qqmlcontext.h" +#include "qqmlcontext_p.h" +#include "qqmlexpression.h" +#include "qqmlimageprovider.h" +#include "qqmlproperty_p.h" +#include "qqmlpropertycache_p.h" +#include "qqmlmetatype_p.h" +#include "qqmldirparser_p.h" +#include <private/qintrusivelist_p.h> +#include <private/qrecyclepool_p.h> + +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qstack.h> +#include <QtCore/qmutex.h> +#include <QtCore/qstring.h> +#include <QtCore/qthread.h> + +#include <private/qobject_p.h> + +#include <private/qv8engine_p.h> +#include <private/qjsengine_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlContext; +class QQmlEngine; +class QQmlContextPrivate; +class QQmlExpression; +class QQmlImportDatabase; +class QNetworkReply; +class QNetworkAccessManager; +class QQmlNetworkAccessManagerFactory; +class QQmlAbstractBinding; +class QQmlTypeNameCache; +class QQmlComponentAttached; +class QQmlCleanup; +class QQmlDelayedError; +class QQuickWorkerScriptEngine; +class QQmlVME; +class QDir; +class QQmlIncubator; + +// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate. +// The inline method definitions are in qqmlexpression_p.h +class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint +{ +public: + inline QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *); + + static inline void endpointCallback(QQmlNotifierEndpoint *); + static inline QQmlJavaScriptExpressionGuard *New(QQmlJavaScriptExpression *e, + QQmlEngine *engine); + inline void Delete(); + + QQmlJavaScriptExpression *expression; + QQmlJavaScriptExpressionGuard *next; +}; + +class Q_QML_EXPORT QQmlEnginePrivate : public QJSEnginePrivate +{ + Q_DECLARE_PUBLIC(QQmlEngine) +public: + QQmlEnginePrivate(QQmlEngine *); + ~QQmlEnginePrivate(); + + void init(); + + class PropertyCapture { + public: + inline virtual ~PropertyCapture() {} + virtual void captureProperty(QQmlNotifier *) = 0; + virtual void captureProperty(QObject *, int, int) = 0; + }; + + PropertyCapture *propertyCapture; + inline void captureProperty(QQmlNotifier *); + inline void captureProperty(QObject *, int, int); + + QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool; + + QQmlContext *rootContext; + bool isDebugging; + + bool outputWarningsToStdErr; + + QQmlContextData *sharedContext; + QObject *sharedScope; + + // Registered cleanup handlers + QQmlCleanup *cleanup; + + // Bindings that have had errors during startup + QQmlDelayedError *erroredBindings; + int inProgressCreations; + + QV8Engine *v8engine() const { return q_func()->handle(); } + + QQuickWorkerScriptEngine *getWorkerScriptEngine(); + QQuickWorkerScriptEngine *workerScriptEngine; + + QUrl baseUrl; + + typedef QPair<QQmlGuard<QObject>,int> FinalizeCallback; + void registerFinalizeCallback(QObject *obj, int index); + + QQmlVME *activeVME; + + QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; + QNetworkAccessManager *getNetworkAccessManager() const; + mutable QNetworkAccessManager *networkAccessManager; + mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory; + + QHash<QString,QSharedPointer<QQmlImageProvider> > imageProviders; + QQmlImageProvider::ImageType getImageProviderType(const QUrl &url); + QQuickTextureFactory *getTextureFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + QImage getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + QPixmap getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + + // Scarce resources are "exceptionally high cost" QVariant types where allowing the + // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other + // out-of-resource situations. When such a resource is passed into JavaScript we + // add it to the scarceResources list and it is destroyed when we return from the + // JavaScript execution that created it. The user can prevent this behavior by + // calling preserve() on the object which removes it from this scarceResource list. + class ScarceResourceData { + public: + ScarceResourceData(const QVariant &data) : data(data) {} + QVariant data; + QIntrusiveListNode node; + }; + QIntrusiveList<ScarceResourceData, &ScarceResourceData::node> scarceResources; + int scarceResourcesRefCount; + void referenceScarceResources(); + void dereferenceScarceResources(); + + QQmlTypeLoader typeLoader; + QQmlImportDatabase importDatabase; + + QString offlineStoragePath; + + mutable quint32 uniqueId; + inline quint32 getUniqueId() const { + return uniqueId++; + } + + QQmlValueTypeFactory valueTypes; + + // Unfortunate workaround to avoid a circular dependency between + // qqmlengine_p.h and qqmlincubator_p.h + struct Incubator { + QIntrusiveListNode next; + // Unfortunate workaround for MSVC + QIntrusiveListNode nextWaitingFor; + }; + QIntrusiveList<Incubator, &Incubator::next> incubatorList; + unsigned int incubatorCount; + QQmlIncubationController *incubationController; + void incubate(QQmlIncubator &, QQmlContextData *); + + // These methods may be called from any thread + inline bool isEngineThread() const; + inline static bool isEngineThread(const QQmlEngine *); + template<typename T> + inline void deleteInEngineThread(T *); + template<typename T> + inline static void deleteInEngineThread(QQmlEngine *, T *); + + // These methods may be called from the loader thread + QQmlMetaType::ModuleApiInstance *moduleApiInstance(const QQmlMetaType::ModuleApi &module); + + // These methods may be called from the loader thread + inline QQmlPropertyCache *cache(QObject *obj); + inline QQmlPropertyCache *cache(const QMetaObject *); + inline QQmlPropertyCache *cache(QQmlType *, int, QQmlError &error); + + // These methods may be called from the loader thread + bool isQObject(int); + QObject *toQObject(const QVariant &, bool *ok = 0) const; + QQmlMetaType::TypeCategory typeCategory(int) const; + bool isList(int) const; + int listType(int) const; + const QMetaObject *rawMetaObjectForType(int) const; + const QMetaObject *metaObjectForType(int) const; + void registerCompositeType(QQmlCompiledData *); + + void sendQuit(); + void warning(const QQmlError &); + void warning(const QList<QQmlError> &); + static void warning(QQmlEngine *, const QQmlError &); + static void warning(QQmlEngine *, const QList<QQmlError> &); + static void warning(QQmlEnginePrivate *, const QQmlError &); + static void warning(QQmlEnginePrivate *, const QList<QQmlError> &); + + inline static QV8Engine *getV8Engine(QQmlEngine *e); + inline static QQmlEnginePrivate *get(QQmlEngine *e); + inline static const QQmlEnginePrivate *get(const QQmlEngine *e); + inline static QQmlEnginePrivate *get(QQmlContext *c); + inline static QQmlEnginePrivate *get(QQmlContextData *c); + inline static QQmlEngine *get(QQmlEnginePrivate *p); + + static QString urlToLocalFileOrQrc(const QUrl& url); + static QString urlToLocalFileOrQrc(const QString& url); + + static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); + static void defineModule(); + + static bool qml_debugging_enabled; + + mutable QMutex mutex; + +private: + // Locker locks the QQmlEnginePrivate data structures for read and write, if necessary. + // Currently, locking is only necessary if the threaded loader is running concurrently. If it is + // either idle, or is running with the main thread blocked, no locking is necessary. This way + // we only pay for locking when we have to. + // Consequently, this class should only be used to protect simple accesses or modifications of the + // QQmlEnginePrivate structures or operations that can be guarenteed not to start activity + // on the loader thread. + // The Locker API is identical to QMutexLocker. Locker reuses the QQmlEnginePrivate::mutex + // QMutex instance and multiple Lockers are recursive in the same thread. + class Locker + { + public: + inline Locker(const QQmlEngine *); + inline Locker(const QQmlEnginePrivate *); + inline ~Locker(); + + inline void unlock(); + inline void relock(); + + private: + const QQmlEnginePrivate *m_ep; + quint32 m_locked:1; + }; + + // Must be called locked + QQmlPropertyCache *createCache(const QMetaObject *); + QQmlPropertyCache *createCache(QQmlType *, int, QQmlError &error); + + // 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<QQmlMetaType::ModuleApi, QQmlMetaType::ModuleApiInstance *> moduleApiInstances; + QHash<const QMetaObject *, QQmlPropertyCache *> propertyCache; + QHash<QPair<QQmlType *, int>, QQmlPropertyCache *> typePropertyCache; + QHash<int, int> m_qmlLists; + QHash<int, QQmlCompiledData *> m_compositeTypes; + + // These members is protected by the full QQmlEnginePrivate::mutex mutex + struct Deletable { Deletable():next(0) {} virtual ~Deletable() {} Deletable *next; }; + QFieldList<Deletable, &Deletable::next> toDeleteInEngineThread; + void doDeleteInEngineThread(); +}; + +QQmlEnginePrivate::Locker::Locker(const QQmlEngine *e) +: m_ep(QQmlEnginePrivate::get(e)) +{ + relock(); +} + +QQmlEnginePrivate::Locker::Locker(const QQmlEnginePrivate *e) +: m_ep(e), m_locked(false) +{ + relock(); +} + +QQmlEnginePrivate::Locker::~Locker() +{ + unlock(); +} + +void QQmlEnginePrivate::Locker::unlock() +{ + if (m_locked) { + m_ep->mutex.unlock(); + m_locked = false; + } +} + +void QQmlEnginePrivate::Locker::relock() +{ + Q_ASSERT(!m_locked); + if (m_ep->typeLoader.isConcurrent()) { + m_ep->mutex.lock(); + m_locked = true; + } +} + +/*! +Returns true if the calling thread is the QQmlEngine thread. +*/ +bool QQmlEnginePrivate::isEngineThread() const +{ + Q_Q(const QQmlEngine); + return QThread::currentThread() == q->thread(); +} + +/*! +Returns true if the calling thread is the QQmlEngine \a engine thread. +*/ +bool QQmlEnginePrivate::isEngineThread(const QQmlEngine *engine) +{ + Q_ASSERT(engine); + return QQmlEnginePrivate::get(engine)->isEngineThread(); +} + +/*! +Delete \a value in the engine thread. If the calling thread is the engine +thread, \a value will be deleted immediately. + +This method should be used for *any* type that has resources that need to +be freed in the engine thread. This is generally types that use V8 handles. +As there is some small overhead in checking the current thread, it is best +practice to check if any V8 handles actually need to be freed and delete +the instance directly if not. +*/ +template<typename T> +void QQmlEnginePrivate::deleteInEngineThread(T *value) +{ + Q_Q(QQmlEngine); + + Q_ASSERT(value); + if (isEngineThread()) { + delete value; + } else { + struct I : public Deletable { + I(T *value) : value(value) {} + ~I() { delete value; } + T *value; + }; + I *i = new I(value); + mutex.lock(); + bool wasEmpty = toDeleteInEngineThread.isEmpty(); + toDeleteInEngineThread.append(i); + mutex.unlock(); + if (wasEmpty) + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +/*! +Delete \a value in the \a engine thread. If the calling thread is the engine +thread, \a value will be deleted immediately. +*/ +template<typename T> +void QQmlEnginePrivate::deleteInEngineThread(QQmlEngine *engine, T *value) +{ + Q_ASSERT(engine); + QQmlEnginePrivate::get(engine)->deleteInEngineThread<T>(value); +} + +/*! +Returns a QQmlPropertyCache for \a obj if one is available. + +If \a obj is null, being deleted or contains a dynamic meta object 0 +is returned. + +The returned cache is not referenced, so if it is to be stored, call addref(). + +XXX thread There is a potential future race condition in this and all the cache() +functions. As the QQmlPropertyCache is returned unreferenced, when called +from the loader thread, it is possible that the cache will have been dereferenced +and deleted before the loader thread has a chance to use or reference it. This +can't currently happen as the cache holds a reference to the +QQmlPropertyCache until the QQmlEngine is destroyed. +*/ +QQmlPropertyCache *QQmlEnginePrivate::cache(QObject *obj) +{ + if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) + return 0; + + Locker locker(this); + const QMetaObject *mo = obj->metaObject(); + QQmlPropertyCache *rv = propertyCache.value(mo); + if (!rv) rv = createCache(mo); + return rv; +} + +/*! +Returns a QQmlPropertyCache for \a metaObject. + +As the cache is persisted for the life of the engine, \a metaObject must be +a static "compile time" meta-object, or a meta-object that is otherwise known to +exist for the lifetime of the QQmlEngine. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QQmlPropertyCache *QQmlEnginePrivate::cache(const QMetaObject *metaObject) +{ + Q_ASSERT(metaObject); + + Locker locker(this); + QQmlPropertyCache *rv = propertyCache.value(metaObject); + if (!rv) rv = createCache(metaObject); + return rv; +} + +/*! +Returns a QQmlPropertyCache for \a type with \a minorVersion. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion, QQmlError &error) +{ + Q_ASSERT(type); + + if (minorVersion == -1 || !type->containsRevisionedAttributes()) + return cache(type->metaObject()); + + Locker locker(this); + QQmlPropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); + if (!rv) rv = createCache(type, minorVersion, error); + return rv; +} + +QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e) +{ + return e->d_func()->v8engine(); +} + +QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlEngine *e) +{ + return e->d_func(); +} + +const QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlEngine *e) +{ + return e->d_func(); +} + +QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContext *c) +{ + return (c && c->engine()) ? QQmlEnginePrivate::get(c->engine()) : 0; +} + +QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContextData *c) +{ + return (c && c->engine) ? QQmlEnginePrivate::get(c->engine) : 0; +} + +QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) +{ + return p->q_func(); +} + +void QQmlEnginePrivate::captureProperty(QQmlNotifier *n) +{ + if (propertyCapture) + propertyCapture->captureProperty(n); +} + +void QQmlEnginePrivate::captureProperty(QObject *o, int c, int n) +{ + if (propertyCapture) + propertyCapture->captureProperty(o, c, n); +} + +QT_END_NAMESPACE + +#endif // QQMLENGINE_P_H diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp new file mode 100644 index 0000000000..79424913f8 --- /dev/null +++ b/src/qml/qml/qqmlerror.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlerror.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlError + \since 4.7 + \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() 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: + QQmlErrorPrivate(); + + QUrl url; + QString description; + int line; + int column; +}; + +QQmlErrorPrivate::QQmlErrorPrivate() +: line(-1), column(-1) +{ +} + +/*! + Creates an empty error object. +*/ +QQmlError::QQmlError() +: d(0) +{ +} + +/*! + Creates a copy of \a other. +*/ +QQmlError::QQmlError(const QQmlError &other) +: d(0) +{ + *this = other; +} + +/*! + Assigns \a other to this error object. +*/ +QQmlError &QQmlError::operator=(const QQmlError &other) +{ + if (!other.d) { + delete d; + d = 0; + } else { + if (!d) d = new QQmlErrorPrivate; + d->url = other.d->url; + d->description = other.d->description; + d->line = other.d->line; + d->column = other.d->column; + } + return *this; +} + +/*! + \internal +*/ +QQmlError::~QQmlError() +{ + delete d; d = 0; +} + +/*! + Returns true if this error is valid, otherwise false. +*/ +bool QQmlError::isValid() const +{ + return d != 0; +} + +/*! + Returns the url for the file that caused this error. +*/ +QUrl QQmlError::url() const +{ + if (d) return d->url; + else 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->description; + else return QString(); +} + +/*! + Sets the error \a description. +*/ +void QQmlError::setDescription(const QString &description) +{ + if (!d) d = new QQmlErrorPrivate; + d->description = description; +} + +/*! + Returns the error line number. +*/ +int QQmlError::line() const +{ + if (d) return d->line; + else return -1; +} + +/*! + Sets the error \a line number. +*/ +void QQmlError::setLine(int line) +{ + if (!d) d = new QQmlErrorPrivate; + d->line = line; +} + +/*! + Returns the error column number. +*/ +int QQmlError::column() const +{ + if (d) return d->column; + else return -1; +} + +/*! + Sets the error \a column number. +*/ +void QQmlError::setColumn(int column) +{ + if (!d) d = new QQmlErrorPrivate; + d->column = column; +} + +/*! + Returns the error as a human readable string. +*/ +QString QQmlError::toString() const +{ + QString rv; + if (url().isEmpty()) { + rv = QLatin1String("<Unknown File>"); + } else if (line() != -1) { + rv = url().toString() + QLatin1Char(':') + QString::number(line()); + if(column() != -1) + rv += QLatin1Char(':') + QString::number(column()); + } else { + rv = url().toString(); + } + + 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")) { + QString file = url.toLocalFile(); + QFile f(file); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QTextStream stream(data, QIODevice::ReadOnly); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + const QString code = stream.readAll(); + const QStringList lines = code.split(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QString &line = lines.at(error.line() - 1); + debug << "\n " << qPrintable(line); + + 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..3c148549d0 --- /dev/null +++ b/src/qml/qml/qqmlerror.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLERROR_H +#define QQMLERROR_H + +#include <QtQml/qtqmlglobal.h> + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +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); + + QString toString() const; +private: + QQmlErrorPrivate *d; +}; + +QDebug Q_QML_EXPORT operator<<(QDebug debug, const QQmlError &error); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLERROR_H diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp new file mode 100644 index 0000000000..3fbb80c280 --- /dev/null +++ b/src/qml/qml/qqmlexpression.cpp @@ -0,0 +1,982 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlexpression.h" +#include "qqmlexpression_p.h" + +#include "qqmlengine_p.h" +#include "qqmlcontext_p.h" +#include "qqmlrewrite_p.h" +#include "qqmlscriptstring_p.h" +#include "qqmlcompiler_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +bool QQmlDelayedError::addError(QQmlEnginePrivate *e) +{ + if (!e) return false; + + if (e->inProgressCreations == 0) return false; // Not in construction + + if (prevError) return true; // Already in error chain + + prevError = &e->erroredBindings; + nextError = e->erroredBindings; + e->erroredBindings = this; + if (nextError) nextError->prevError = &nextError; + + return true; +} + +QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v) +: m_vtable(v) +{ +} + +QQmlJavaScriptExpression::~QQmlJavaScriptExpression() +{ + clearGuards(); +} + +static QQmlJavaScriptExpression::VTable QDeclarativeExpressionPrivate_jsvtable = { + QQmlExpressionPrivate::expressionIdentifier, + QQmlExpressionPrivate::expressionChanged +}; + +QQmlExpressionPrivate::QQmlExpressionPrivate() +: QQmlJavaScriptExpression(&QDeclarativeExpressionPrivate_jsvtable), + expressionFunctionValid(true), expressionFunctionRewritten(false), + extractExpressionFromFunction(false), line(-1), dataRef(0) +{ +} + +QQmlExpressionPrivate::~QQmlExpressionPrivate() +{ + qPersistentDispose(v8qmlscope); + qPersistentDispose(v8function); + if (dataRef) dataRef->release(); + dataRef = 0; +} + +void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, + QObject *me) +{ + expression = expr; + + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(me); + expressionFunctionValid = false; + expressionFunctionRewritten = false; +} + +void QQmlExpressionPrivate::init(QQmlContextData *ctxt, v8::Handle<v8::Function> func, + QObject *me) +{ + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(me); + + v8function = qPersistentNew<v8::Function>(func); + setUseSharedContext(false); + expressionFunctionValid = true; + expressionFunctionRewritten = false; + extractExpressionFromFunction = true; +} + +void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, + bool isRewritten, QObject *me, const QString &srcUrl, + int lineNumber, int columnNumber) +{ + url = srcUrl; + line = lineNumber; + column = columnNumber; + + expression = expr; + + expressionFunctionValid = false; + expressionFunctionRewritten = isRewritten; + + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(me); +} + +void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr, + bool isRewritten, QObject *me, const QString &srcUrl, + int lineNumber, int columnNumber) +{ + url = srcUrl; + line = lineNumber; + column = columnNumber; + + if (isRewritten) { + expressionFunctionValid = true; + expressionFunctionRewritten = true; + v8function = evalFunction(ctxt, me, expr.constData(), expr.length(), + srcUrl, lineNumber, &v8qmlscope); + setUseSharedContext(false); + + expressionUtf8 = expr; + } else { + expression = QString::fromUtf8(expr); + + expressionFunctionValid = false; + expressionFunctionRewritten = isRewritten; + } + + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(me); +} + +// Callee owns the persistent handle +v8::Persistent<v8::Function> +QQmlExpressionPrivate::evalFunction(QQmlContextData *ctxt, QObject *scope, + const char *code, int codeLength, + const QString &filename, int line, + v8::Persistent<v8::Object> *qmlscope) +{ + QQmlEngine *engine = ctxt->engine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + + v8::HandleScope handle_scope; + v8::Context::Scope ctxtscope(ep->v8engine()->context()); + + v8::TryCatch tc; + v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope); + v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, codeLength, filename, line); + if (tc.HasCaught()) { + QQmlError error; + error.setDescription(QLatin1String("Exception occurred during function compilation")); + error.setLine(line); + error.setUrl(QUrl::fromLocalFile(filename)); + v8::Local<v8::Message> message = tc.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + ep->warning(error); + return v8::Persistent<v8::Function>(); + } + v8::Local<v8::Value> result = script->Run(scopeobject); + if (tc.HasCaught()) { + QQmlError error; + error.setDescription(QLatin1String("Exception occurred during function evaluation")); + error.setLine(line); + error.setUrl(QUrl::fromLocalFile(filename)); + v8::Local<v8::Message> message = tc.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + ep->warning(error); + return v8::Persistent<v8::Function>(); + } + if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject); + return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result)); +} + +// Callee owns the persistent handle +v8::Persistent<v8::Function> +QQmlExpressionPrivate::evalFunction(QQmlContextData *ctxt, QObject *scope, + const QString &code, const QString &filename, int line, + v8::Persistent<v8::Object> *qmlscope) +{ + QQmlEngine *engine = ctxt->engine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + + v8::HandleScope handle_scope; + v8::Context::Scope ctxtscope(ep->v8engine()->context()); + + v8::TryCatch tc; + v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope); + v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line); + if (tc.HasCaught()) { + QQmlError error; + error.setDescription(QLatin1String("Exception occurred during function compilation")); + error.setLine(line); + error.setUrl(QUrl::fromLocalFile(filename)); + v8::Local<v8::Message> message = tc.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + ep->warning(error); + return v8::Persistent<v8::Function>(); + } + v8::Local<v8::Value> result = script->Run(scopeobject); + if (tc.HasCaught()) { + QQmlError error; + error.setDescription(QLatin1String("Exception occurred during function evaluation")); + error.setLine(line); + error.setUrl(QUrl::fromLocalFile(filename)); + v8::Local<v8::Message> message = tc.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + ep->warning(error); + return v8::Persistent<v8::Function>(); + } + if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject); + return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result)); +} + +QQmlExpression * +QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object, + const QString &expr, bool isRewritten, + const QString &url, int lineNumber, int columnNumber) +{ + return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber, *new QQmlExpressionPrivate); +} + +/*! + \class QQmlExpression + \since 4.7 + \brief The QQmlExpression class evaluates JavaScript in a QML context. + + For example, given a file \c main.qml like this: + + \qml + import QtQuick 2.0 + + Item { + width: 200; height: 200 + } + \endqml + + The following code evaluates a JavaScript expression in the context of the + above QML: + + \code + QQmlEngine *engine = new QQmlEngine; + QQmlComponent component(engine, QUrl::fromLocalFile("main.qml")); + + QObject *myObject = component.create(); + QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2"); + int result = expr->evaluate().toInt(); // result = 400 + \endcode +*/ + +/*! + Create an invalid QQmlExpression. + + As the expression will not have an associated QQmlContext, this will be a + null expression object and its value will always be an invalid QVariant. + */ +QQmlExpression::QQmlExpression() +: QObject(*new QQmlExpressionPrivate, 0) +{ +} + +/*! \internal */ +QQmlExpression::QQmlExpression(QQmlContextData *ctxt, + QObject *object, const QString &expr, bool isRewritten, + const QString &url, int lineNumber, int columnNumber, + QQmlExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QQmlExpression); + d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber); +} + +/*! \internal */ +QQmlExpression::QQmlExpression(QQmlContextData *ctxt, + QObject *object, const QByteArray &expr, + bool isRewritten, + const QString &url, int lineNumber, int columnNumber, + QQmlExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QQmlExpression); + d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber); +} + +/*! + Create a QQmlExpression object that is a child of \a parent. + + The \script provides the expression to be evaluated, the context to evaluate it in, + and the scope object to evaluate it with. + + This constructor is functionally equivalent to the following, but in most cases + is more efficient. + \code + QQmlExpression expression(script.context(), script.scopeObject(), script.script(), parent); + \endcode + + \sa QQmlScriptString +*/ +QQmlExpression::QQmlExpression(const QQmlScriptString &script, QObject *parent) +: QObject(*new QQmlExpressionPrivate, parent) +{ + Q_D(QQmlExpression); + bool defaultConstruction = false; + + int id = script.d.data()->bindingId; + if (id < 0) { + defaultConstruction = true; + } else { + QQmlContextData *ctxtdata = QQmlContextData::get(script.context()); + + QQmlEnginePrivate *engine = QQmlEnginePrivate::get(script.context()->engine()); + QQmlCompiledData *cdata = 0; + QQmlTypeData *typeData = 0; + if (engine && ctxtdata && !ctxtdata->url.isEmpty()) { + typeData = engine->typeLoader.get(ctxtdata->url); + cdata = typeData->compiledData(); + } + + if (cdata) + d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(), + cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber); + else + defaultConstruction = true; + + if (cdata) + cdata->release(); + if (typeData) + typeData->release(); + } + + if (defaultConstruction) + d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject()); +} + +/*! + Create a QQmlExpression object that is a child of \a parent. + + The \a expression JavaScript will be executed in the \a ctxt QQmlContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QQmlExpression::QQmlExpression(QQmlContext *ctxt, + QObject *scope, + const QString &expression, + QObject *parent) +: QObject(*new QQmlExpressionPrivate, parent) +{ + Q_D(QQmlExpression); + d->init(QQmlContextData::get(ctxt), expression, scope); +} + +/*! + \internal +*/ +QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, + const QString &expression) +: QObject(*new QQmlExpressionPrivate, 0) +{ + Q_D(QQmlExpression); + d->init(ctxt, expression, scope); +} + +/*! \internal */ +QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, + const QString &expression, QQmlExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QQmlExpression); + d->init(ctxt, expression, scope); +} + +/*! + \internal + + To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>. + For example: + v8::Handle<v8::Function> function; + new QQmlExpression(ctxt, scope, &function, ...); + */ +QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, void *functionPtr, + QQmlExpressionPrivate &dd) +: QObject(dd, 0) +{ + v8::Handle<v8::Function> function = *(v8::Handle<v8::Function> *)functionPtr; + + Q_D(QQmlExpression); + d->init(ctxt, function, scope); +} + +/*! + Destroy the QQmlExpression instance. +*/ +QQmlExpression::~QQmlExpression() +{ +} + +/*! + Returns the QQmlEngine this expression is associated with, or 0 if there + is no association or the QQmlEngine has been destroyed. +*/ +QQmlEngine *QQmlExpression::engine() const +{ + Q_D(const QQmlExpression); + return d->context()?d->context()->engine:0; +} + +/*! + Returns the QQmlContext this expression is associated with, or 0 if there + is no association or the QQmlContext has been destroyed. +*/ +QQmlContext *QQmlExpression::context() const +{ + Q_D(const QQmlExpression); + QQmlContextData *data = d->context(); + return data?data->asQQmlContext():0; +} + +/*! + Returns the expression string. +*/ +QString QQmlExpression::expression() const +{ + Q_D(const QQmlExpression); + if (d->extractExpressionFromFunction && context()->engine()) { + QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(context()->engine()); + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + + return v8engine->toString(v8::Handle<v8::Value>(d->v8function)); + } else if (!d->expressionUtf8.isEmpty()) { + return QString::fromUtf8(d->expressionUtf8); + } else { + return d->expression; + } +} + +/*! + Set the expression to \a expression. +*/ +void QQmlExpression::setExpression(const QString &expression) +{ + Q_D(QQmlExpression); + + d->resetNotifyOnValueChanged(); + d->expression = expression; + d->expressionUtf8.clear(); + d->expressionFunctionValid = false; + d->expressionFunctionRewritten = false; + qPersistentDispose(d->v8function); + qPersistentDispose(d->v8qmlscope); +} + +void QQmlExpressionPrivate::exceptionToError(v8::Handle<v8::Message> message, + QQmlError &error) +{ + Q_ASSERT(!message.IsEmpty()); + + v8::Handle<v8::Value> name = message->GetScriptResourceName(); + v8::Handle<v8::String> description = message->Get(); + int lineNumber = message->GetLineNumber(); + + v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>(); + if (file.IsEmpty() || file->Length() == 0) + error.setUrl(QUrl(QLatin1String("<Unknown File>"))); + else + error.setUrl(QUrl(QV8Engine::toStringStatic(file))); + + error.setLine(lineNumber); + error.setColumn(-1); + + QString qDescription = QV8Engine::toStringStatic(description); + if (qDescription.startsWith(QLatin1String("Uncaught "))) + qDescription = qDescription.mid(9 /* strlen("Uncaught ") */); + + error.setDescription(qDescription); +} + +void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) +{ + activeGuards.setFlagValue(v); + if (!v) clearGuards(); +} + +void QQmlJavaScriptExpression::resetNotifyOnValueChanged() +{ + clearGuards(); +} + +v8::Local<v8::Value> +QQmlJavaScriptExpression::evaluate(QQmlContextData *context, + v8::Handle<v8::Function> function, bool *isUndefined) +{ + Q_ASSERT(context && context->engine); + + if (function.IsEmpty() || function->IsUndefined()) { + if (isUndefined) *isUndefined = true; + return v8::Local<v8::Value>(); + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty()); + GuardCapture capture(context->engine, this); + + QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture; + ep->propertyCapture = notifyOnValueChanged()?&capture:0; + + + if (notifyOnValueChanged()) + capture.guards.copyAndClearPrepend(activeGuards); + + QQmlContextData *lastSharedContext = 0; + QObject *lastSharedScope = 0; + + bool sharedContext = useSharedContext(); + + // All code that follows must check with watcher before it accesses data members + // incase we have been deleted. + DeleteWatcher watcher(this); + + if (sharedContext) { + lastSharedContext = ep->sharedContext; + lastSharedScope = ep->sharedScope; + ep->sharedContext = context; + ep->sharedScope = scopeObject(); + } + + v8::Local<v8::Value> result; + { + v8::TryCatch try_catch; + v8::Handle<v8::Object> This = ep->v8engine()->global(); + if (scopeObject() && requiresThisObject()) { + v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject()); + if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value); + } + + result = function->Call(This, 0, 0); + + if (isUndefined) + *isUndefined = try_catch.HasCaught() || result->IsUndefined(); + + if (watcher.wasDeleted()) { + } else if (try_catch.HasCaught()) { + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) { + QQmlExpressionPrivate::exceptionToError(message, delayedError()->error); + } else { + if (hasDelayedError()) delayedError()->error = QQmlError(); + } + } else { + if (hasDelayedError()) delayedError()->error = QQmlError(); + } + } + + if (sharedContext) { + ep->sharedContext = lastSharedContext; + ep->sharedScope = lastSharedScope; + } + + if (capture.errorString) { + for (int ii = 0; ii < capture.errorString->count(); ++ii) + qWarning("%s", qPrintable(capture.errorString->at(ii))); + delete capture.errorString; + capture.errorString = 0; + } + + while (Guard *g = capture.guards.takeFirst()) + g->Delete(); + + ep->propertyCapture = lastPropertyCapture; + + return result; +} + +void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) +{ + if (expression) { + + // Try and find a matching guard + while (!guards.isEmpty() && !guards.first()->isConnected(n)) + guards.takeFirst()->Delete(); + + Guard *g = 0; + if (!guards.isEmpty()) { + g = guards.takeFirst(); + g->cancelNotify(); + Q_ASSERT(g->isConnected(n)); + } else { + g = Guard::New(expression, engine); + g->connect(n); + } + + expression->activeGuards.prepend(g); + } +} + +void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n) +{ + if (expression) { + if (n == -1) { + if (!errorString) { + errorString = new QStringList; + QString preamble = QLatin1String("QQmlExpression: Expression ") + + expression->m_vtable->expressionIdentifier(expression) + + QLatin1String(" depends on non-NOTIFYable properties:"); + errorString->append(preamble); + } + + const QMetaObject *metaObj = o->metaObject(); + QMetaProperty metaProp = metaObj->property(c); + + QString error = QLatin1String(" ") + + QString::fromUtf8(metaObj->className()) + + QLatin1String("::") + + QString::fromUtf8(metaProp.name()); + errorString->append(error); + } else { + + // Try and find a matching guard + while (!guards.isEmpty() && !guards.first()->isConnected(o, n)) + guards.takeFirst()->Delete(); + + Guard *g = 0; + if (!guards.isEmpty()) { + g = guards.takeFirst(); + g->cancelNotify(); + Q_ASSERT(g->isConnected(o, n)); + } else { + g = Guard::New(expression, engine); + g->connect(o, n); + } + + expression->activeGuards.prepend(g); + } + } +} + +void QQmlJavaScriptExpression::clearError() +{ + if (m_vtable.hasValue()) { + m_vtable.value().error = QQmlError(); + m_vtable.value().removeError(); + } +} + +QQmlError QQmlJavaScriptExpression::error() const +{ + if (m_vtable.hasValue()) return m_vtable.constValue()->error; + else return QQmlError(); +} + +QQmlDelayedError *QQmlJavaScriptExpression::delayedError() +{ + return &m_vtable.value(); +} + +void QQmlJavaScriptExpression::clearGuards() +{ + while (Guard *g = activeGuards.takeFirst()) + g->Delete(); +} + +// Must be called with a valid handle scope +v8::Local<v8::Value> QQmlExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined) +{ + if (!expressionFunctionValid) { + bool ok = true; + + QQmlRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName(name); + QString code; + if (expressionFunctionRewritten) + code = expression; + else + code = rewriteBinding(expression, &ok); + + if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope); + setUseSharedContext(false); + expressionFunctionValid = true; + } + + + if (secondaryScope) { + v8::Local<v8::Value> result; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); + QObject *restoreSecondaryScope = 0; + restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope); + result = evaluate(context(), v8function, isUndefined); + ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope); + return result; + } else { + return evaluate(context(), v8function, isUndefined); + } +} + +QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined) +{ + Q_Q(QQmlExpression); + + if (!context() || !context()->isValid()) { + qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context"); + return QVariant(); + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine()); + QVariant rv; + + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. + + { + v8::HandleScope handle_scope; + v8::Context::Scope context_scope(ep->v8engine()->context()); + v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined); + rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >()); + } + + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. + + return rv; +} + +/*! + Evaulates the expression, returning the result of the evaluation, + or an invalid QVariant if the expression is invalid or has an error. + + \a valueIsUndefined is set to true if the expression resulted in an + undefined value. + + \sa hasError(), error() +*/ +QVariant QQmlExpression::evaluate(bool *valueIsUndefined) +{ + Q_D(QQmlExpression); + return d->value(0, valueIsUndefined); +} + +/*! +Returns true if the valueChanged() signal is emitted when the expression's evaluated +value changes. +*/ +bool QQmlExpression::notifyOnValueChanged() const +{ + Q_D(const QQmlExpression); + return d->notifyOnValueChanged(); +} + +/*! + Sets whether the valueChanged() signal is emitted when the + expression's evaluated value changes. + + If \a notifyOnChange is true, the QQmlExpression will + monitor properties involved in the expression's evaluation, and emit + QQmlExpression::valueChanged() if they have changed. This + allows an application to ensure that any value associated with the + result of the expression remains up to date. + + If \a notifyOnChange is false (default), the QQmlExpression + will not montitor properties involved in the expression's + evaluation, and QQmlExpression::valueChanged() will never be + emitted. This is more efficient if an application wants a "one off" + evaluation of the expression. +*/ +void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange) +{ + Q_D(QQmlExpression); + d->setNotifyOnValueChanged(notifyOnChange); +} + +/*! + Returns the source file URL for this expression. The source location must + have been previously set by calling setSourceLocation(). +*/ +QString QQmlExpression::sourceFile() const +{ + Q_D(const QQmlExpression); + return d->url; +} + +/*! + Returns the source file line number for this expression. The source location + must have been previously set by calling setSourceLocation(). +*/ +int QQmlExpression::lineNumber() const +{ + Q_D(const QQmlExpression); + return d->line; +} + +/*! + Returns the source file column number for this expression. The source location + must have been previously set by calling setSourceLocation(). +*/ +int QQmlExpression::columnNumber() const +{ + Q_D(const QQmlExpression); + return d->column; +} + +/*! + Set the location of this expression to \a line of \a url. This information + is used by the script engine. +*/ +void QQmlExpression::setSourceLocation(const QString &url, int line, int column) +{ + Q_D(QQmlExpression); + d->url = url; + d->line = line; + d->column = column; +} + +/*! + Returns the expression's scope object, if provided, otherwise 0. + + In addition to data provided by the expression's QQmlContext, the scope + object's properties are also in scope during the expression's evaluation. +*/ +QObject *QQmlExpression::scopeObject() const +{ + Q_D(const QQmlExpression); + return d->scopeObject(); +} + +/*! + Returns true if the last call to evaluate() resulted in an error, + otherwise false. + + \sa error(), clearError() +*/ +bool QQmlExpression::hasError() const +{ + Q_D(const QQmlExpression); + return d->hasError(); +} + +/*! + Clear any expression errors. Calls to hasError() following this will + return false. + + \sa hasError(), error() +*/ +void QQmlExpression::clearError() +{ + Q_D(QQmlExpression); + d->clearError(); +} + +/*! + Return any error from the last call to evaluate(). If there was no error, + this returns an invalid QQmlError instance. + + \sa hasError(), clearError() +*/ + +QQmlError QQmlExpression::error() const +{ + Q_D(const QQmlExpression); + return d->error(); +} + +/*! + \fn void QQmlExpression::valueChanged() + + Emitted each time the expression value changes from the last time it was + evaluated. The expression must have been evaluated at least once (by + calling QQmlExpression::evaluate()) before this signal will be emitted. +*/ + +void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e) +{ + QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); + This->expressionChanged(); +} + +void QQmlExpressionPrivate::expressionChanged() +{ + Q_Q(QQmlExpression); + emit q->valueChanged(); +} + +QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e) +{ + QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); + return QLatin1String("\"") + This->expression + QLatin1String("\""); +} + +QQmlAbstractExpression::QQmlAbstractExpression() +: m_prevExpression(0), m_nextExpression(0) +{ +} + +QQmlAbstractExpression::~QQmlAbstractExpression() +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + } + + if (m_context.isT2()) + m_context.asT2()->_s = 0; +} + +QQmlContextData *QQmlAbstractExpression::context() const +{ + if (m_context.isT1()) return m_context.asT1(); + else return m_context.asT2()->_c; +} + +void QQmlAbstractExpression::setContext(QQmlContextData *context) +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + m_prevExpression = 0; + m_nextExpression = 0; + } + + if (m_context.isT1()) m_context = context; + else m_context.asT2()->_c = context; + + if (context) { + m_nextExpression = context->expressions; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = &context->expressions; + context->expressions = this; + } +} + +void QQmlAbstractExpression::refresh() +{ +} + +bool QQmlAbstractExpression::isValid() const +{ + return context() != 0; +} + +QT_END_NAMESPACE + +#include <moc_qqmlexpression.cpp> diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h new file mode 100644 index 0000000000..4044546fbf --- /dev/null +++ b/src/qml/qml/qqmlexpression.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLEXPRESSION_H +#define QQMLEXPRESSION_H + +#include <QtQml/qqmlerror.h> +#include <QtQml/qqmlscriptstring.h> + +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QString; +class QQmlRefCount; +class QQmlEngine; +class QQmlContext; +class QQmlExpressionPrivate; +class QQmlContextData; +class Q_QML_EXPORT QQmlExpression : public QObject +{ + Q_OBJECT +public: + QQmlExpression(); + QQmlExpression(QQmlContext *, QObject *, const QString &, QObject * = 0); + explicit QQmlExpression(const QQmlScriptString &, QObject * = 0); + virtual ~QQmlExpression(); + + QQmlEngine *engine() const; + QQmlContext *context() const; + + QString expression() const; + void setExpression(const QString &); + + bool notifyOnValueChanged() const; + void setNotifyOnValueChanged(bool); + + QString sourceFile() const; + int lineNumber() const; + int columnNumber() const; + void setSourceLocation(const QString &fileName, int line, int column = 0); + + QObject *scopeObject() const; + + bool hasError() const; + void clearError(); + QQmlError error() const; + + QVariant evaluate(bool *valueIsUndefined = 0); + +Q_SIGNALS: + void valueChanged(); + +protected: + QQmlExpression(QQmlContextData *, QObject *, const QString &, + QQmlExpressionPrivate &dd); + QQmlExpression(QQmlContextData *, QObject *, void *, + QQmlExpressionPrivate &dd); + QQmlExpression(QQmlContextData *, QObject *, const QString &, bool, + const QString &, int, int, QQmlExpressionPrivate &dd); + QQmlExpression(QQmlContextData *, QObject *, const QByteArray &, bool, + const QString &, int, int, QQmlExpressionPrivate &dd); + +private: + QQmlExpression(QQmlContextData *, QObject *, const QString &); + + Q_DISABLE_COPY(QQmlExpression) + Q_DECLARE_PRIVATE(QQmlExpression) + friend class QQmlDebugger; + friend class QQmlContext; + friend class QQmlVME; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLEXPRESSION_H + diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h new file mode 100644 index 0000000000..deca29ab60 --- /dev/null +++ b/src/qml/qml/qqmlexpression_p.h @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLEXPRESSION_P_H +#define QQMLEXPRESSION_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 "qqmlexpression.h" + +#include <private/qv8engine_p.h> +#include <private/qfieldlist_p.h> +#include <private/qflagpointer_p.h> +#include <private/qdeletewatcher_p.h> +#include <private/qqmlguard_p.h> +#include <private/qpointervaluepair_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlAbstractExpression +{ +public: + QQmlAbstractExpression(); + virtual ~QQmlAbstractExpression(); + + bool isValid() const; + + QQmlContextData *context() const; + void setContext(QQmlContextData *); + + virtual void refresh(); + + class DeleteWatcher { + public: + inline DeleteWatcher(QQmlAbstractExpression *); + inline ~DeleteWatcher(); + inline bool wasDeleted() const; + private: + friend class QQmlAbstractExpression; + QQmlContextData *_c; + QQmlAbstractExpression **_w; + QQmlAbstractExpression *_s; + }; + +private: + friend class QQmlContext; + friend class QQmlContextData; + friend class QQmlContextPrivate; + + QBiPointer<QQmlContextData, DeleteWatcher> m_context; + QQmlAbstractExpression **m_prevExpression; + QQmlAbstractExpression *m_nextExpression; +}; + +class QQmlDelayedError +{ +public: + inline QQmlDelayedError() : nextError(0), prevError(0) {} + inline ~QQmlDelayedError() { removeError(); } + + QQmlError error; + + bool addError(QQmlEnginePrivate *); + + inline void removeError() { + if (!prevError) return; + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = 0; + prevError = 0; + } + +private: + QQmlDelayedError *nextError; + QQmlDelayedError **prevError; +}; + +class QQmlJavaScriptExpression +{ +public: + // Although this looks crazy, we implement our own "vtable" here, rather than relying on + // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage + // location that is use for the vtable to also store the rarely used delayed error. + // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in + // memory for every expression. + struct VTable { + QString (*expressionIdentifier)(QQmlJavaScriptExpression *); + void (*expressionChanged)(QQmlJavaScriptExpression *); + }; + + QQmlJavaScriptExpression(VTable *vtable); + + v8::Local<v8::Value> evaluate(QQmlContextData *, v8::Handle<v8::Function>, + bool *isUndefined); + + inline bool requiresThisObject() const; + inline void setRequiresThisObject(bool v); + inline bool useSharedContext() const; + inline void setUseSharedContext(bool v); + inline bool notifyOnValueChanged() const; + + void setNotifyOnValueChanged(bool v); + void resetNotifyOnValueChanged(); + + inline QObject *scopeObject() const; + inline void setScopeObject(QObject *v); + + class DeleteWatcher { + public: + inline DeleteWatcher(QQmlJavaScriptExpression *); + inline ~DeleteWatcher(); + inline bool wasDeleted() const; + private: + friend class QQmlJavaScriptExpression; + QObject *_c; + QQmlJavaScriptExpression **_w; + QQmlJavaScriptExpression *_s; + }; + + inline bool hasError() const; + inline bool hasDelayedError() const; + QQmlError error() const; + void clearError(); + QQmlDelayedError *delayedError(); + +protected: + ~QQmlJavaScriptExpression(); + +private: + typedef QQmlJavaScriptExpressionGuard Guard; + friend class QQmlJavaScriptExpressionGuard; + + struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { + GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e) + : engine(engine), expression(e), errorString(0) { } + + ~GuardCapture() { + Q_ASSERT(guards.isEmpty()); + Q_ASSERT(errorString == 0); + } + + virtual void captureProperty(QQmlNotifier *); + virtual void captureProperty(QObject *, int, int); + + QQmlEngine *engine; + QQmlJavaScriptExpression *expression; + QFieldList<Guard, &Guard::next> guards; + QStringList *errorString; + }; + + QPointerValuePair<VTable, QQmlDelayedError> m_vtable; + + // We store some flag bits in the following flag pointers. + // m_scopeObject:flag1 - requiresThisObject + // activeGuards:flag1 - notifyOnValueChanged + // activeGuards:flag2 - useSharedContext + QBiPointer<QObject, DeleteWatcher> m_scopeObject; + QForwardFieldList<Guard, &Guard::next> activeGuards; + + void clearGuards(); +}; + +class QQmlExpression; +class QString; +class Q_QML_PRIVATE_EXPORT QQmlExpressionPrivate : public QObjectPrivate, public QQmlJavaScriptExpression, public QQmlAbstractExpression +{ + Q_DECLARE_PUBLIC(QQmlExpression) +public: + QQmlExpressionPrivate(); + ~QQmlExpressionPrivate(); + + void init(QQmlContextData *, const QString &, QObject *); + void init(QQmlContextData *, v8::Handle<v8::Function>, QObject *); + void init(QQmlContextData *, const QString &, bool, QObject *, const QString &, int, int); + void init(QQmlContextData *, const QByteArray &, bool, QObject *, const QString &, int, int); + + QVariant value(QObject *secondaryScope = 0, bool *isUndefined = 0); + + v8::Local<v8::Value> v8value(QObject *secondaryScope = 0, bool *isUndefined = 0); + + static inline QQmlExpressionPrivate *get(QQmlExpression *expr); + static inline QQmlExpression *get(QQmlExpressionPrivate *expr); + + void _q_notify(); + + static void exceptionToError(v8::Handle<v8::Message>, QQmlError &); + static v8::Persistent<v8::Function> evalFunction(QQmlContextData *ctxt, QObject *scope, + const QString &code, const QString &filename, + int line, + v8::Persistent<v8::Object> *qmlscope = 0); + static v8::Persistent<v8::Function> evalFunction(QQmlContextData *ctxt, QObject *scope, + const char *code, int codeLength, + const QString &filename, int line, + v8::Persistent<v8::Object> *qmlscope = 0); + + static QQmlExpression *create(QQmlContextData *, QObject *, const QString &, bool, + const QString &, int, int); + + bool expressionFunctionValid:1; + bool expressionFunctionRewritten:1; + bool extractExpressionFromFunction:1; + + // "Inherited" from QQmlJavaScriptExpression + static QString expressionIdentifier(QQmlJavaScriptExpression *); + static void expressionChanged(QQmlJavaScriptExpression *); + virtual void expressionChanged(); + + QString expression; + QByteArray expressionUtf8; + + v8::Persistent<v8::Object> v8qmlscope; + v8::Persistent<v8::Function> v8function; + + QString url; // This is a QString for a reason. QUrls are slooooooow... + int line; + int column; + QString name; //function name, hint for the debugger + + QQmlRefCount *dataRef; +}; + +QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) +: _c(0), _w(0), _s(e) +{ + if (e->m_context.isT1()) { + _w = &_s; + _c = e->m_context.asT1(); + e->m_context = this; + } else { + // Another watcher is already registered + _w = &e->m_context.asT2()->_s; + } +} + +QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() +{ + Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); + if (*_w && _s->m_context.asT2() == this) + _s->m_context = _c; +} + +bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const +{ + return *_w == 0; +} + +QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) +: _c(0), _w(0), _s(e) +{ + if (e->m_scopeObject.isT1()) { + _w = &_s; + _c = e->m_scopeObject.asT1(); + e->m_scopeObject = this; + } else { + // Another watcher is already registered + _w = &e->m_scopeObject.asT2()->_s; + } +} + +QQmlJavaScriptExpression::DeleteWatcher::~DeleteWatcher() +{ + Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_scopeObject.isT2())); + if (*_w && _s->m_scopeObject.asT2() == this) + _s->m_scopeObject = _c; +} + +bool QQmlJavaScriptExpression::DeleteWatcher::wasDeleted() const +{ + return *_w == 0; +} + +bool QQmlJavaScriptExpression::requiresThisObject() const +{ + return m_scopeObject.flag(); +} + +void QQmlJavaScriptExpression::setRequiresThisObject(bool v) +{ + m_scopeObject.setFlagValue(v); +} + +bool QQmlJavaScriptExpression::useSharedContext() const +{ + return activeGuards.flag2(); +} + +void QQmlJavaScriptExpression::setUseSharedContext(bool v) +{ + activeGuards.setFlag2Value(v); +} + +bool QQmlJavaScriptExpression::notifyOnValueChanged() const +{ + return activeGuards.flag(); +} + +QObject *QQmlJavaScriptExpression::scopeObject() const +{ + if (m_scopeObject.isT1()) return m_scopeObject.asT1(); + else return m_scopeObject.asT2()->_c; +} + +void QQmlJavaScriptExpression::setScopeObject(QObject *v) +{ + if (m_scopeObject.isT1()) m_scopeObject = v; + else m_scopeObject.asT2()->_c = v; +} + +bool QQmlJavaScriptExpression::hasError() const +{ + return m_vtable.hasValue() && m_vtable.constValue()->error.isValid(); +} + +bool QQmlJavaScriptExpression::hasDelayedError() const +{ + return m_vtable.hasValue(); +} + +QQmlExpressionPrivate *QQmlExpressionPrivate::get(QQmlExpression *expr) +{ + return static_cast<QQmlExpressionPrivate *>(QObjectPrivate::get(expr)); +} + +QQmlExpression *QQmlExpressionPrivate::get(QQmlExpressionPrivate *expr) +{ + return expr->q_func(); +} + +QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) +: expression(e), next(0) +{ + callback = &endpointCallback; +} + +void QQmlJavaScriptExpressionGuard::endpointCallback(QQmlNotifierEndpoint *e) +{ + QQmlJavaScriptExpression *expression = + static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression; + + expression->m_vtable->expressionChanged(expression); +} + +QQmlJavaScriptExpressionGuard * +QQmlJavaScriptExpressionGuard::New(QQmlJavaScriptExpression *e, + QQmlEngine *engine) +{ + Q_ASSERT(e); + return QQmlEnginePrivate::get(engine)->jsExpressionGuardPool.New(e); +} + +void QQmlJavaScriptExpressionGuard::Delete() +{ + QRecyclePool<QQmlJavaScriptExpressionGuard>::Delete(this); +} + +QT_END_NAMESPACE + +#endif // QQMLEXPRESSION_P_H diff --git a/src/qml/qml/qqmlextensioninterface.h b/src/qml/qml/qqmlextensioninterface.h new file mode 100644 index 0000000000..63ef1adc08 --- /dev/null +++ b/src/qml/qml/qqmlextensioninterface.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLEXTENSIONINTERFACE_H +#define QQMLEXTENSIONINTERFACE_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlEngine; + +class Q_QML_EXPORT QQmlTypesExtensionInterface +{ +public: + virtual ~QQmlTypesExtensionInterface() {} + virtual void registerTypes(const char *uri) = 0; +}; + +class Q_QML_EXPORT QQmlExtensionInterface : public QQmlTypesExtensionInterface +{ +public: + virtual ~QQmlExtensionInterface() {} + virtual void initializeEngine(QQmlEngine *engine, const char *uri) = 0; +}; + +Q_DECLARE_INTERFACE(QQmlTypesExtensionInterface, "org.qt-project.Qt.QQmlTypesExtensionInterface/1.0") +Q_DECLARE_INTERFACE(QQmlExtensionInterface, "org.qt-project.Qt.QQmlExtensionInterface/1.0") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLEXTENSIONINTERFACE_H diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp new file mode 100644 index 0000000000..c3d8f0b34f --- /dev/null +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlextensionplugin.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.7 + \class QQmlExtensionPlugin + \brief The QQmlExtensionPlugin class provides an abstract base for custom QML extension plugins. + + \ingroup plugins + + QQmlExtensionPlugin is a plugin interface that makes it possible to + create QML extensions that can be loaded dynamically into QML applications. + These extensions allow custom QML types to be made available to the QML engine. + + To write a QML extension plugin: + + \list + \o Subclass QQmlExtensionPlugin, implement registerTypes() method + to register types using qmlRegisterType(), and export the class using the Q_EXPORT_PLUGIN2() macro + \o Write an appropriate project file for the plugin + \o Create a \l{Writing a qmldir file}{qmldir file} to describe the plugin + \endlist + + QML extension plugins can be used to provide either application-specific or + library-like plugins. Library plugins should limit themselves to registering types, + as any manipulation of the engine's root context may cause conflicts + or other issues in the library user's code. + + + \section1 An example + + Suppose there is a new \c TimeModel C++ class that should be made available + as a new QML element. It provides the current time through \c hour and \c minute + properties, like this: + + \snippet examples/declarative/cppextensions/plugins/plugin.cpp 0 + \dots + + To make this class available as a QML type, create a plugin that registers + this type with a specific \l {QML Modules}{module} using qmlRegisterType(). For this example the plugin + module will be named \c com.nokia.TimeExample (as defined in the project + file further below). + + \snippet examples/declarative/cppextensions/plugins/plugin.cpp plugin + \codeline + \snippet examples/declarative/cppextensions/plugins/plugin.cpp export + + This registers the \c TimeModel class with the 1.0 version of this + plugin library, as a QML type called \c Time. The Q_ASSERT statement + ensures the module is imported correctly by any QML components that use this plugin. + + The project file defines the project as a plugin library and specifies + it should be built into the \c com/nokia/TimeExample directory: + + \code + TEMPLATE = lib + CONFIG += qt plugin + QT += declarative + + DESTDIR = com/nokia/TimeExample + TARGET = qmlqtimeexampleplugin + ... + \endcode + + Finally, a \l{Writing a qmldir file}{qmldir file} is required in the \c com/nokia/TimeExample directory + that describes the plugin. This directory includes a \c Clock.qml file that + should be bundled with the plugin, so it needs to be specified in the \c qmldir + file: + + \quotefile examples/declarative/cppextensions/plugins/com/nokia/TimeExample/qmldir + + Once the project is built and installed, the new \c Time element can be + used by any QML component that imports the \c com.nokia.TimeExample module: + + \snippet examples/declarative/cppextensions/plugins/plugins.qml 0 + + The full source code is available in the \l {declarative/cppextensions/plugins}{plugins example}. + + The \l {Tutorial: Writing QML extensions with C++} also contains a chapter + on creating QML plugins. + + \sa QQmlEngine::importPlugin(), {How to Create Qt Plugins} +*/ + +/*! + \fn void QQmlExtensionPlugin::registerTypes(const char *uri) + + Registers the QML types in the given \a uri. Subclasses should implement + this to call qmlRegisterType() for all types which are provided by the extension + plugin. + + The \a uri is an identifier for the plugin generated by the QML engine + based on the name and path of the extension's plugin library. +*/ + +/*! + Constructs a QML extension plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QQmlExtensionPlugin::QQmlExtensionPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + \internal + */ +QQmlExtensionPlugin::~QQmlExtensionPlugin() +{ +} + +/*! + \fn void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri) + + Initializes the extension from the \a uri using the \a engine. Here an application + plugin might, for example, expose some data or objects to QML, + as context properties on the engine's root context. +*/ + +void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri) +{ + Q_UNUSED(engine); + Q_UNUSED(uri); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlextensionplugin.h b/src/qml/qml/qqmlextensionplugin.h new file mode 100644 index 0000000000..91a9e95869 --- /dev/null +++ b/src/qml/qml/qqmlextensionplugin.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLEXTENSIONPLUGIN_H +#define QQMLEXTENSIONPLUGIN_H + +#include <QtCore/qplugin.h> + +#include <QtQml/qqmlextensioninterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlEngine; + +class Q_QML_EXPORT QQmlExtensionPlugin : public QObject, + public QQmlExtensionInterface +{ + Q_OBJECT + Q_INTERFACES(QQmlExtensionInterface) + Q_INTERFACES(QQmlTypesExtensionInterface) +public: + explicit QQmlExtensionPlugin(QObject *parent = 0); + ~QQmlExtensionPlugin(); + + virtual void registerTypes(const char *uri) = 0; + virtual void initializeEngine(QQmlEngine *engine, const char *uri); + +private: + Q_DISABLE_COPY(QQmlExtensionPlugin) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLEXTENSIONPLUGIN_H diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h new file mode 100644 index 0000000000..c41b823e60 --- /dev/null +++ b/src/qml/qml/qqmlglobal_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLGLOBAL_H +#define QQMLGLOBAL_H + +#include <QtCore/qglobal.h> +#include <QtCore/QObject> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +#define DEFINE_BOOL_CONFIG_OPTION(name, var) \ + static bool name() \ + { \ + static enum { Yes, No, Unknown } status = Unknown; \ + if (status == Unknown) { \ + QByteArray v = qgetenv(#var); \ + bool value = !v.isEmpty() && v != "0" && v != "false"; \ + if (value) status = Yes; \ + else status = No; \ + } \ + return status == Yes; \ + } + +#define FAST_CONNECT(Sender, Signal, Receiver, Method) \ +{ \ + QObject *sender = (Sender); \ + QObject *receiver = (Receiver); \ + const char *signal = (Signal); \ + const char *method = (Method); \ + static int signalIdx = -1; \ + static int methodIdx = -1; \ + if (signalIdx < 0) { \ + if (((int)(*signal) - '0') == QSIGNAL_CODE) \ + signalIdx = sender->metaObject()->indexOfSignal(signal+1); \ + else \ + qWarning("FAST_CONNECT: Invalid signal %s. Please make sure you are using the SIGNAL macro.", signal); \ + } \ + if (methodIdx < 0) { \ + int code = ((int)(*method) - '0'); \ + if (code == QSLOT_CODE) \ + methodIdx = receiver->metaObject()->indexOfSlot(method+1); \ + else if (code == QSIGNAL_CODE) \ + methodIdx = receiver->metaObject()->indexOfSignal(method+1); \ + else \ + qWarning("FAST_CONNECT: Invalid method %s. Please make sure you are using the SIGNAL or SLOT macro.", method); \ + } \ + QMetaObject::connect(sender, signalIdx, receiver, methodIdx, Qt::DirectConnection); \ +} + +struct QQmlGraphics_DerivedObject : public QObject +{ + void setParent_noEvent(QObject *parent) { + bool sce = d_ptr->sendChildEvents; + d_ptr->sendChildEvents = false; + setParent(parent); + d_ptr->sendChildEvents = sce; + } +}; + +/*! + Returns true if the case of \a fileName is equivalent to the file case of + \a fileName on disk, and false otherwise. + + This is used to ensure that the behavior of QML on a case-insensitive file + system is the same as on a case-sensitive file system. This function + performs a "best effort" attempt to determine the real case of the file. + It may have false positives (say the case is correct when it isn't), but it + should never have a false negative (say the case is incorrect when it is + correct). +*/ +bool QQml_isFileCaseCorrect(const QString &fileName); + +/*! + Makes the \a object a child of \a parent. Note that when using this method, + neither \a parent nor the object's previous parent (if it had one) will + receive ChildRemoved or ChildAdded events. +*/ +inline void QQml_setParent_noEvent(QObject *object, QObject *parent) +{ + static_cast<QQmlGraphics_DerivedObject *>(object)->setParent_noEvent(parent); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLGLOBAL_H diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h new file mode 100644 index 0000000000..b4e7408fae --- /dev/null +++ b/src/qml/qml/qqmlguard_p.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLGUARD_P_H +#define QQMLGUARD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. 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 <private/qqmldata_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlGuardImpl +{ +public: + inline QQmlGuardImpl(); + inline QQmlGuardImpl(QObject *); + inline QQmlGuardImpl(const QQmlGuardImpl &); + inline ~QQmlGuardImpl(); + + QObject *o; + QQmlGuardImpl *next; + QQmlGuardImpl **prev; + + inline void addGuard(); + inline void remGuard(); +}; + +class QObject; +template<class T> +class QQmlGuard : private QQmlGuardImpl +{ + friend class QQmlData; +public: + inline QQmlGuard(); + inline QQmlGuard(T *); + inline QQmlGuard(const QQmlGuard<T> &); + inline virtual ~QQmlGuard(); + + inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o); + inline QQmlGuard<T> &operator=(T *); + + inline T *object() const; + inline void setObject(T *g); + + inline bool isNull() const + { return !o; } + + inline T* operator->() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + inline T& operator*() const + { return *static_cast<T*>(const_cast<QObject*>(o)); } + inline operator T*() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + inline T* data() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + +protected: + virtual void objectDestroyed(T *) {} +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlGuard<QObject>) + +QT_BEGIN_NAMESPACE + +QQmlGuardImpl::QQmlGuardImpl() +: o(0), next(0), prev(0) +{ +} + +QQmlGuardImpl::QQmlGuardImpl(QObject *g) +: o(g), next(0), prev(0) +{ + if (o) addGuard(); +} + +QQmlGuardImpl::QQmlGuardImpl(const QQmlGuardImpl &g) +: o(g.o), next(0), prev(0) +{ + if (o) addGuard(); +} + +QQmlGuardImpl::~QQmlGuardImpl() +{ + if (prev) remGuard(); + o = 0; +} + +void QQmlGuardImpl::addGuard() +{ + Q_ASSERT(!prev); + + if (QObjectPrivate::get(o)->wasDeleted) + return; + + QQmlData *data = QQmlData::get(o, true); + next = data->guards; + if (next) next->prev = &next; + data->guards = this; + prev = &data->guards; +} + +void QQmlGuardImpl::remGuard() +{ + Q_ASSERT(prev); + + if (next) next->prev = prev; + *prev = next; + next = 0; + prev = 0; +} + +template<class T> +QQmlGuard<T>::QQmlGuard() +{ +} + +template<class T> +QQmlGuard<T>::QQmlGuard(T *g) +: QQmlGuardImpl(g) +{ +} + +template<class T> +QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g) +: QQmlGuardImpl(g) +{ +} + +template<class T> +QQmlGuard<T>::~QQmlGuard() +{ +} + +template<class T> +QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g) +{ + setObject(g.object()); + return *this; +} + +template<class T> +QQmlGuard<T> &QQmlGuard<T>::operator=(T *g) +{ + setObject(g); + return *this; +} + +template<class T> +T *QQmlGuard<T>::object() const +{ + return static_cast<T *>(o); +}; + +template<class T> +void QQmlGuard<T>::setObject(T *g) +{ + if (g != o) { + if (prev) remGuard(); + o = g; + if (o) addGuard(); + } +} + +QT_END_NAMESPACE + +#endif // QQMLGUARD_P_H diff --git a/src/qml/qml/qqmlimageprovider.cpp b/src/qml/qml/qqmlimageprovider.cpp new file mode 100644 index 0000000000..863093033a --- /dev/null +++ b/src/qml/qml/qqmlimageprovider.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlimageprovider.h" + +QT_BEGIN_NAMESPACE + +class QQmlImageProviderPrivate +{ +public: + QQmlImageProvider::ImageType type; +}; + +/*! + \class QQuickTextureFactory + \since 5.0 + \brief The QQuickTextureFactory class provides an interface for loading custom textures from QML. + + The purpose of the texture factory is to provide a placeholder for a image + data that can be converted into an OpenGL texture. + + Creating a texture directly is not possible as there is rarely an OpenGL context + available in the thread that is responsible for loading the image data. + */ + +QQuickTextureFactory::QQuickTextureFactory() +{ +} + +QQuickTextureFactory::~QQuickTextureFactory() +{ +} + + + +/*! + \fn QSGTexture *QQuickTextureFactory::createTexture() const + + This function is called on the scene graph rendering thread to create a QSGTexture + instance from the factory. + + QML will internally cache the returned texture as needed. Each call to this + function should return a unique instance. + + The OpenGL context used for rendering is bound when this function is called. + */ + +/*! + \fn QSize QQuickTextureFactory::textureSize() const + + Returns the size of the texture. This function will be called from arbitrary threads + and should not rely on an OpenGL context bound. + */ + + +/*! + \class QQmlImageProvider + \since 4.7 + \brief The QQmlImageProvider class provides an interface for supporting pixmaps and threaded image requests in QML. + + QQmlImageProvider is used to provide advanced image loading features + in QML applications. It allows images in QML to be: + + \list + \o Loaded using QPixmaps rather than actual image files + \o Loaded asynchronously in a separate thread, if imageType() is \l{QQmlImageProvider::ImageType}{ImageType::Image} + \endlist + + To specify that an image should be loaded by an image provider, use the + \bold {"image:"} scheme for the URL source of the image, followed by the + identifiers of the image provider and the requested image. For example: + + \qml + Image { source: "image://myimageprovider/image.png" } + \endqml + + This specifies that the image should be loaded by the image provider named + "myimageprovider", and the image to be loaded is named "image.png". The QML engine + invokes the appropriate image provider according to the providers that have + been registered through QQmlEngine::addImageProvider(). + + Note that the identifiers are case-insensitive, but the rest of the URL will be passed on with + preserved case. For example, the below snippet would still specify that the image is loaded by the + image provider named "myimageprovider", but it would request a different image than the above snippet + ("Image.png" instead of "image.png"). + \qml + Image { source: "image://MyImageProvider/Image.png" } + \endqml + + If you want the rest of the URL to be case insensitive, you will have to take care + of that yourself inside your image provider. + + \section2 An example + + Here are two images. Their \c source values indicate they should be loaded by + an image provider named "colors", and the images to be loaded are "yellow" + and "red", respectively: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider-example.qml 0 + + When these images are loaded by QML, it looks for a matching image provider + and calls its requestImage() or requestPixmap() method (depending on its + imageType()) to load the image. The method is called with the \c id + parameter set to "yellow" for the first image, and "red" for the second. + + Here is an image provider implementation that can load the images + requested by the above QML. This implementation dynamically + generates QPixmap images that are filled with the requested color: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 0 + \codeline + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 1 + + To make this provider accessible to QML, it is registered with the QML engine + with a "colors" identifier: + + \code + int main(int argc, char *argv[]) + { + ... + + QQmlEngine engine; + engine->addImageProvider(QLatin1String("colors"), new ColorPixmapProvider); + + ... + } + \endcode + + Now the images can be successfully loaded in QML: + + \image imageprovider.png + + A complete example is available in Qt's + \l {declarative/cppextensions/imageprovider}{examples/declarative/cppextensions/imageprovider} + directory. Note the example registers the provider via a \l{QQmlExtensionPlugin}{plugin} + instead of registering it in the application \c main() function as shown above. + + + \section2 Asynchronous image loading + + Image providers that support QImage loading automatically include support + for asychronous loading of images. To enable asynchronous loading for an + image source, set the \c asynchronous property to \c true for the relevant + \l Image, \l BorderImage or \l AnimatedImage object. When this is enabled, + the image request to the provider is run in a low priority thread, + allowing image loading to be executed in the background, and reducing the + performance impact on the user interface. + + Asynchronous loading is not supported for image providers that provide + QPixmap rather than QImage values, as pixmaps can only be created in the + main thread. In this case, if \l {Image::}{asynchronous} is set to + \c true, the value is ignored and the image is loaded + synchronously. + + + \section2 Image caching + + Images returned by a QQmlImageProvider are automatically cached, + similar to any image loaded by the QML engine. When an image with a + "image://" prefix is loaded from cache, requestImage() and requestPixmap() + will not be called for the relevant image provider. If an image should always + be fetched from the image provider, and should not be cached at all, set the + \c cache property to \c false for the relevant \l Image, \l BorderImage or + \l AnimatedImage object. + + \sa QQmlEngine::addImageProvider() +*/ + +/*! + \enum QQmlImageProvider::ImageType + + Defines the type of image supported by this image provider. + + \value Image The Image Provider provides QImage images. The + requestImage() method will be called for all image requests. + \value Pixmap The Image Provider provides QPixmap images. The + requestPixmap() method will be called for all image requests. + \value Texture The Image Provider provides QSGTextureProvider based images. + The requestTexture() method will be called for all image requests. \omitvalue +*/ + +/*! + Creates an image provider that will provide images of the given \a type. +*/ +QQmlImageProvider::QQmlImageProvider(ImageType type) + : d(new QQmlImageProviderPrivate) +{ + d->type = type; +} + +/*! + Destroys the QQmlImageProvider + + \note The destructor of your derived class need to be thread safe. +*/ +QQmlImageProvider::~QQmlImageProvider() +{ + delete d; +} + +/*! + Returns the image type supported by this provider. +*/ +QQmlImageProvider::ImageType QQmlImageProvider::imageType() const +{ + return d->type; +} + +/*! + Implement this method to return the image with \a id. The default + implementation returns an empty image. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ +QImage QQmlImageProvider::requestImage(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Image) + qWarning("ImageProvider supports Image type but has not implemented requestImage()"); + return QImage(); +} + +/*! + Implement this method to return the pixmap with \a id. The default + implementation returns an empty pixmap. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. +*/ +QPixmap QQmlImageProvider::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Pixmap) + qWarning("ImageProvider supports Pixmap type but has not implemented requestPixmap()"); + return QPixmap(); +} + + +/*! + Implement this method to return the texture with \a id. The default + implementation returns 0. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +QQuickTextureFactory *QQmlImageProvider::requestTexture(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Texture) + qWarning("ImageProvider supports Texture type but has not implemented requestTexture()"); + return 0; +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmlimageprovider.h b/src/qml/qml/qqmlimageprovider.h new file mode 100644 index 0000000000..d59cfc42ca --- /dev/null +++ b/src/qml/qml/qqmlimageprovider.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLIMAGEPROVIDER_H +#define QQMLIMAGEPROVIDER_H + +#include <QtQml/qtqmlglobal.h> +#include <QtGui/qimage.h> +#include <QtGui/qpixmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlImageProviderPrivate; +class QSGTexture; +class QQuickCanvas; + +class Q_QML_EXPORT QQuickTextureFactory : public QObject +{ +public: + QQuickTextureFactory(); + ~QQuickTextureFactory(); + + virtual QSGTexture *createTexture(QQuickCanvas *canvas) const = 0; + virtual QSize textureSize() const = 0; + virtual int textureByteCount() const = 0; +}; + +class Q_QML_EXPORT QQmlImageProvider +{ +public: + enum ImageType { + Image, + Pixmap, + Texture, + Invalid + }; + + QQmlImageProvider(ImageType type); + virtual ~QQmlImageProvider(); + + ImageType imageType() const; + + virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); + virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize); + +private: + QQmlImageProviderPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLIMAGEPROVIDER diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp new file mode 100644 index 0000000000..6e74536fa3 --- /dev/null +++ b/src/qml/qml/qqmlimport.cpp @@ -0,0 +1,1183 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlimport_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpluginloader.h> +#include <QtCore/qlibraryinfo.h> +#include <QtQml/qqmlextensioninterface.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmltypenamecache_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE) +DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) + +static bool greaterThan(const QString &s1, const QString &s2) +{ + return s1 > s2; +} + +QString resolveLocalUrl(const QString &url, const QString &relative) +{ + if (relative.contains(QLatin1Char(':'))) { + // contains a host name + return QUrl(url).resolved(QUrl(relative)).toString(); + } else if (relative.isEmpty()) { + return url; + } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) { + return relative; + } else { + if (relative == QLatin1String(".")) + return url.left(url.lastIndexOf(QLatin1Char('/')) + 1); + else if (relative.startsWith(QLatin1String("./"))) + return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2); + return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative; + } +} + + + +typedef QMap<QString, QString> StringStringMap; +Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri + +class QQmlImportedNamespace +{ +public: + struct Data { + QString uri; + QString url; + int majversion; + int minversion; + bool isLibrary; + QQmlDirComponents qmlDirComponents; + QQmlDirScripts qmlDirScripts; + }; + QList<Data> imports; + + + bool find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor, + QQmlType** type_return, QString* url_return, + QString *base = 0, bool *typeRecursionDetected = 0); + bool find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return, + QString* url_return, QString *base = 0, QList<QQmlError> *errors = 0); +}; + +class QQmlImportsPrivate { +public: + QQmlImportsPrivate(QQmlTypeLoader *loader); + ~QQmlImportsPrivate(); + + bool importExtension(const QString &absoluteFilePath, const QString &uri, + QQmlImportDatabase *database, QQmlDirComponents* components, + QQmlDirScripts *scripts, + QList<QQmlError> *errors); + + QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); + bool add(const QQmlDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, + int vmaj, int vmin, QQmlScript::Import::Type importType, + QQmlImportDatabase *database, QList<QQmlError> *errors); + bool find(const QString& type, int *vmajor, int *vminor, + QQmlType** type_return, QString* url_return, QList<QQmlError> *errors); + + QQmlImportedNamespace *findNamespace(const QString& type); + + QUrl baseUrl; + QString base; + int ref; + + QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded; + QQmlImportedNamespace unqualifiedset; + QHash<QString,QQmlImportedNamespace* > set; + QQmlTypeLoader *typeLoader; +}; + +/*! +\class QQmlImports +\brief The QQmlImports class encapsulates one QML document's import statements. +\internal +*/ +QQmlImports::QQmlImports(const QQmlImports ©) +: d(copy.d) +{ + ++d->ref; +} + +QQmlImports & +QQmlImports::operator =(const QQmlImports ©) +{ + ++copy.d->ref; + if (--d->ref == 0) + delete d; + d = copy.d; + return *this; +} + +QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader) + : d(new QQmlImportsPrivate(typeLoader)) +{ +} + +QQmlImports::~QQmlImports() +{ + if (--d->ref == 0) + delete d; +} + +/*! + Sets the base URL to be used for all relative file imports added. +*/ +void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString) +{ + d->baseUrl = url; + + if (urlString.isEmpty()) { + d->base = url.toString(); + } else { + //Q_ASSERT(url.toString() == urlString); + d->base = urlString; + } +} + +/*! + Returns the base URL to be used for all relative file imports added. +*/ +QUrl QQmlImports::baseUrl() const +{ + return d->baseUrl; +} + +void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const +{ + const QQmlImportedNamespace &set = d->unqualifiedset; + + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { + const QQmlImportedNamespace::Data &data = set.imports.at(ii); + QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion); + if (module) + cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, data.minversion)); + } + + for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.begin(); + iter != d->set.end(); + ++iter) { + + const QQmlImportedNamespace &set = *iter.value(); + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { + const QQmlImportedNamespace::Data &data = set.imports.at(ii); + QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion); + if (module) { + QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()]; + import.modules.append(QQmlTypeModuleVersion(module, data.minversion)); + } + + QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(data.uri, data.majversion, data.minversion); + if (moduleApi.script || moduleApi.qobject) { + QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()]; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + import.moduleApi = ep->moduleApiInstance(moduleApi); + } + } + } +} + +QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const +{ + QList<QQmlImports::ScriptReference> scripts; + + const QQmlImportedNamespace &set = d->unqualifiedset; + + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { + const QQmlImportedNamespace::Data &data = set.imports.at(ii); + + foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) { + ScriptReference ref; + ref.nameSpace = script.nameSpace; + ref.location = QUrl(data.url).resolved(QUrl(script.fileName)); + scripts.append(ref); + } + } + + for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.constBegin(); + iter != d->set.constEnd(); + ++iter) { + const QQmlImportedNamespace &set = *iter.value(); + + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { + const QQmlImportedNamespace::Data &data = set.imports.at(ii); + + foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) { + ScriptReference ref; + ref.nameSpace = script.nameSpace; + ref.qualifier = iter.key(); + ref.location = QUrl(data.url).resolved(QUrl(script.fileName)); + scripts.append(ref); + } + } + } + + return scripts; +} + +/*! + \internal + + The given (namespace qualified) \a type is resolved to either + \list + \o a QQmlImportedNamespace stored at \a ns_return, + \o a QQmlType stored at \a type_return, or + \o a component located at \a url_return. + \endlist + + If any return pointer is 0, the corresponding search is not done. + + \sa addImport() +*/ +bool QQmlImports::resolveType(const QString& type, + QQmlType** type_return, QString* url_return, int *vmaj, int *vmin, + QQmlImportedNamespace** ns_return, QList<QQmlError> *errors) const +{ + QQmlImportedNamespace* ns = d->findNamespace(type); + if (ns) { + if (ns_return) + *ns_return = ns; + return true; + } + if (type_return || url_return) { + if (d->find(type,vmaj,vmin,type_return,url_return, errors)) { + if (qmlImportTrace()) { + if (type_return && *type_return && url_return && !url_return->isEmpty()) + qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << (*type_return)->typeName() << " " << *url_return; + if (type_return && *type_return) + qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << (*type_return)->typeName(); + if (url_return && !url_return->isEmpty()) + qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << *url_return; + } + return true; + } + } + return false; +} + +/*! + \internal + + Searching \e only in the namespace \a ns (previously returned in a call to + resolveType(), \a type is found and returned to either + a QQmlType stored at \a type_return, or + a component located at \a url_return. + + If either return pointer is 0, the corresponding search is not done. +*/ +bool QQmlImports::resolveType(QQmlImportedNamespace* ns, const QString& type, + QQmlType** type_return, QString* url_return, + int *vmaj, int *vmin) const +{ + return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return); +} + +bool QQmlImportedNamespace::find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor, + QQmlType** type_return, QString* url_return, + QString *base, bool *typeRecursionDetected) +{ + int vmaj = data.majversion; + int vmin = data.minversion; + + if (vmaj >= 0 && vmin >= 0) { + QString qt = data.uri + QLatin1Char('/') + type; + QQmlType *t = QQmlMetaType::qmlType(qt,vmaj,vmin); + if (t) { + if (vmajor) *vmajor = vmaj; + if (vminor) *vminor = vmin; + if (type_return) + *type_return = t; + return true; + } + } + + const QQmlDirComponents &qmldircomponents = data.qmlDirComponents; + bool typeWasDeclaredInQmldir = false; + if (!qmldircomponents.isEmpty()) { + foreach (const QQmlDirParser::Component &c, qmldircomponents) { + if (c.typeName == type) { + typeWasDeclaredInQmldir = true; + // importing version -1 means import ALL versions + if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) { + QString url(data.url + type + QLatin1String(".qml")); + QString candidate = resolveLocalUrl(url, c.fileName); + if (c.internal && base) { + if (resolveLocalUrl(*base, c.fileName) != candidate) + continue; // failed attempt to access an internal type + } + if (base && *base == candidate) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } + if (url_return) + *url_return = candidate; + return true; + } + } + } + } + + if (!typeWasDeclaredInQmldir && !data.isLibrary) { + // XXX search non-files too! (eg. zip files, see QT-524) + QString url(data.url + type + QLatin1String(".qml")); + QString file = QQmlEnginePrivate::urlToLocalFileOrQrc(url); + if (!typeLoader->absoluteFilePath(file).isEmpty()) { + if (base && *base == url) { // no recursion + if (typeRecursionDetected) + *typeRecursionDetected = true; + } else { + if (url_return) + *url_return = url; + return true; + } + } + } + return false; +} + +QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader) + : ref(1), typeLoader(loader) +{ +} + +QQmlImportsPrivate::~QQmlImportsPrivate() +{ + foreach (QQmlImportedNamespace* s, set.values()) + delete s; +} + +bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, + QQmlImportDatabase *database, + QQmlDirComponents* components, + QQmlDirScripts* scripts, + QList<QQmlError> *errors) +{ + const QQmlDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath); + if (qmldirParser->hasError()) { + if (errors) { + const QList<QQmlError> qmldirErrors = qmldirParser->errors(uri); + for (int i = 0; i < qmldirErrors.size(); ++i) + errors->prepend(qmldirErrors.at(i)); + } + return false; + } + + if (qmlImportTrace()) + qDebug().nospace() << "QQmlImports(" << qPrintable(base) << "::importExtension: " + << "loaded " << absoluteFilePath; + + if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { + qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); + + QString qmldirPath = absoluteFilePath; + int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/')); + if (slash > 0) + qmldirPath.truncate(slash); + foreach (const QQmlDirParser::Plugin &plugin, qmldirParser->plugins()) { + + QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); + if (!resolvedFilePath.isEmpty()) { + if (!database->importPlugin(resolvedFilePath, uri, errors)) { + if (errors) { + // 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(QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description())); + error.setUrl(QUrl::fromLocalFile(absoluteFilePath)); + errors->prepend(error); + } + return false; + } + } else { + if (errors) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name)); + error.setUrl(QUrl::fromLocalFile(absoluteFilePath)); + errors->prepend(error); + } + return false; + } + } + } + + if (components) + *components = qmldirParser->components(); + if (scripts) + *scripts = qmldirParser->scripts(); + + return true; +} + +QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database) +{ + QString dir = dir_arg; + if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\'))) + dir.chop(1); + + QStringList paths = database->fileImportPath; + qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents. + + QString stableRelativePath = dir; + foreach(const QString &path, paths) { + if (dir.startsWith(path)) { + stableRelativePath = dir.mid(path.length()+1); + break; + } + } + + stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/')); + + // remove optional versioning in dot notation from uri + int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/')); + if (lastSlash >= 0) { + int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash); + if (versionDot >= 0) + stableRelativePath = stableRelativePath.left(versionDot); + } + + stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); + return stableRelativePath; +} + +bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, int vmaj, int vmin, + QQmlScript::Import::Type importType, + QQmlImportDatabase *database, QList<QQmlError> *errors) +{ + static QLatin1String Slash_qmldir("/qmldir"); + static QLatin1Char Slash('/'); + + QQmlDirComponents qmldircomponents = qmldircomponentsnetwork; + QQmlDirScripts qmldirscripts; + QString uri = uri_arg; + QQmlImportedNamespace *s; + if (prefix.isEmpty()) { + s = &unqualifiedset; + } else { + s = set.value(prefix); + if (!s) + set.insert(prefix,(s=new QQmlImportedNamespace)); + } + QString url = uri; + bool versionFound = false; + if (importType == QQmlScript::Import::Library) { + + Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries + + url.replace(QLatin1Char('.'), Slash); + bool found = false; + QString dir; + QString qmldir; + + // step 1: search for extension with fully encoded version number + foreach (const QString &p, database->fileImportPath) { + dir = p+Slash+url; + + QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); + + if (fi.isFile()) { + found = true; + + const QString absolutePath = fi.absolutePath(); + if (absolutePath.at(0) == QLatin1Char(':')) + url = QLatin1String("qrc://") + absolutePath.mid(1); + else + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) + return false; + break; + } + } + + // TODO: Should this search be omitted if found == true? + + // step 2: search for extension with encoded version major + foreach (const QString &p, database->fileImportPath) { + dir = p+Slash+url; + + QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); + + if (fi.isFile()) { + found = true; + + const QString absolutePath = fi.absolutePath(); + if (absolutePath.at(0) == QLatin1Char(':')) + url = QLatin1String("qrc://") + absolutePath.mid(1); + else + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) + return false; + break; + } + } + + if (!found) { + // step 3: search for extension without version number + + foreach (const QString &p, database->fileImportPath) { + dir = p+Slash+url; + qmldir = dir+Slash_qmldir; + + QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir); + if (!absoluteFilePath.isEmpty()) { + found = true; + QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1); + if (absolutePath.at(0) == QLatin1Char(':')) + url = QLatin1String("qrc://") + absolutePath.mid(1); + else + url = QUrl::fromLocalFile(absolutePath).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) + return false; + break; + } + } + } + + if (QQmlMetaType::isModule(uri, vmaj, vmin)) + versionFound = true; + + if (!versionFound && qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) { + if (errors) { + QQmlError error; // we don't set the url or line or column as these will be set by the loader. + if (QQmlMetaType::isAnyModule(uri)) + error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin)); + else + error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg)); + errors->prepend(error); + } + return false; + } + } else { + if (importType == QQmlScript::Import::File && qmldircomponents.isEmpty()) { + QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir); + QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl); + if (!localFileOrQrc.isEmpty()) { + QString dir = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri)); + if (!typeLoader->directoryExists(dir)) { + if (errors) { + QQmlError error; // we don't set the line or column as these will be set by the loader. + error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri_arg)); + error.setUrl(QUrl(importUrl)); + errors->prepend(error); + } + return false; // local import dirs must exist + } + uri = resolvedUri(dir, database); + if (uri.endsWith(Slash)) + uri.chop(1); + if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) { + if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors)) + return false; + } + } else { + if (prefix.isEmpty()) { + // directory must at least exist for valid import + QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri)); + if (!typeLoader->directoryExists(localFileOrQrc)) { + if (errors) { + QQmlError error; // we don't set the line or column as these will be set by the loader. + if (localFileOrQrc.isEmpty()) + error.setDescription(QQmlImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri)); + else + error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri)); + error.setUrl(QUrl(importUrl)); + errors->prepend(error); + } + return false; + } + } + } + } + + url = resolveLocalUrl(base, url); + } + + if (!versionFound && (vmaj > -1) && (vmin > -1) && !qmldircomponents.isEmpty()) { + int lowest_min = INT_MAX; + int highest_min = INT_MIN; + + QList<QQmlDirParser::Component>::const_iterator cend = qmldircomponents.constEnd(); + for (QList<QQmlDirParser::Component>::const_iterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) { + if (cit->majorVersion == vmaj) { + lowest_min = qMin(lowest_min, cit->minorVersion); + highest_min = qMax(highest_min, cit->minorVersion); + } + } + + if (lowest_min > vmin || highest_min < vmin) { + if (errors) { + QQmlError error; // we don't set the url or line or column information, as these will be set by the loader. + error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin)); + errors->prepend(error); + } + return false; + } + } + + if (!url.endsWith(Slash)) + url += Slash; + + QMap<QString, QQmlDirParser::Script> scripts; + + if (!qmldirscripts.isEmpty()) { + // Verify that we haven't imported these scripts already + QList<QQmlImportedNamespace::Data>::const_iterator end = s->imports.constEnd(); + for (QList<QQmlImportedNamespace::Data>::const_iterator it = s->imports.constBegin(); it != end; ++it) { + if (it->uri == uri) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url)); + errors->prepend(error); + return false; + } + } + + QList<QQmlDirParser::Script>::const_iterator send = qmldirscripts.constEnd(); + for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin(); sit != send; ++sit) { + // Only include scripts that match our requested version + if (((vmaj == -1) || (sit->majorVersion == vmaj)) && + ((vmin == -1) || (sit->minorVersion <= vmin))) { + + // Load the highest version that matches + QMap<QString, QQmlDirParser::Script>::iterator it = scripts.find(sit->nameSpace); + if (it == scripts.end() || (it->minorVersion < sit->minorVersion)) { + scripts.insert(sit->nameSpace, *sit); + } + } + } + } + + QQmlImportedNamespace::Data data; + data.uri = uri; + data.url = url; + data.majversion = vmaj; + data.minversion = vmin; + data.isLibrary = importType == QQmlScript::Import::Library; + data.qmlDirComponents = qmldircomponents; + data.qmlDirScripts = scripts.values(); + + s->imports.prepend(data); + + return true; +} + +bool QQmlImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QQmlType** type_return, + QString* url_return, QList<QQmlError> *errors) +{ + QQmlImportedNamespace *s = 0; + int slash = type.indexOf(QLatin1Char('/')); + if (slash >= 0) { + QString namespaceName = type.left(slash); + s = set.value(namespaceName); + if (!s) { + if (errors) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName)); + errors->prepend(error); + } + return false; + } + int nslash = type.indexOf(QLatin1Char('/'),slash+1); + if (nslash > 0) { + if (errors) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed")); + errors->prepend(error); + } + return false; + } + } else { + s = &unqualifiedset; + } + QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) + if (s) { + if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors)) + return true; + if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) { + // qualified, and only 1 url + *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml")); + return true; + } + } + + return false; +} + +QQmlImportedNamespace *QQmlImportsPrivate::findNamespace(const QString& type) +{ + return set.value(type); +} + +bool QQmlImportedNamespace::find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return, + QString* url_return, QString *base, QList<QQmlError> *errors) +{ + bool typeRecursionDetected = false; + for (int i=0; i<imports.count(); ++i) { + if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) { + if (qmlCheckTypes()) { + // check for type clashes + for (int j = i+1; j<imports.count(); ++j) { + if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) { + if (errors) { + QString u1 = imports.at(i).url; + QString u2 = imports.at(j).url; + if (base) { + QString b = *base; + int slash = b.lastIndexOf(QLatin1Char('/')); + if (slash >= 0) { + b = b.left(slash+1); + QString l = b.left(slash); + if (u1.startsWith(b)) + u1 = u1.mid(b.count()); + else if (u1 == l) + u1 = QQmlImportDatabase::tr("local directory"); + if (u2.startsWith(b)) + u2 = u2.mid(b.count()); + else if (u2 == l) + u2 = QQmlImportDatabase::tr("local directory"); + } + } + + QQmlError error; + if (u1 != u2) { + error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2)); + } else { + error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") + .arg(u1) + .arg(imports.at(i).majversion).arg(imports.at(i).minversion) + .arg(imports.at(j).majversion).arg(imports.at(j).minversion)); + } + errors->prepend(error); + } + return false; + } + } + } + return true; + } + } + if (errors) { + QQmlError error; + if (typeRecursionDetected) + error.setDescription(QQmlImportDatabase::tr("is instantiated recursively")); + else + error.setDescription(QQmlImportDatabase::tr("is not a type")); + errors->prepend(error); + } + return false; +} + +/*! +\class QQmlImportDatabase +\brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine. +\internal +*/ +QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e) +: engine(e) +{ + filePluginPath << QLatin1String("."); + + // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath + +#ifndef QT_NO_SETTINGS + QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath); + addImportPath(installImportsPath); +#endif // QT_NO_SETTINGS + + // env import paths + QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); + if (!envImportPath.isEmpty()) { +#if defined(Q_OS_WIN) + QLatin1Char pathSep(';'); +#else + QLatin1Char pathSep(':'); +#endif + QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts); + for (int ii = paths.count() - 1; ii >= 0; --ii) + addImportPath(paths.at(ii)); + } + + addImportPath(QCoreApplication::applicationDirPath()); +} + +QQmlImportDatabase::~QQmlImportDatabase() +{ +} + +/*! + \internal + + Adds information to \a imports such that subsequent calls to resolveType() + will resolve types qualified by \a prefix by considering types found at the given \a uri. + + The uri is either a directory (if importType is FileImport), or a URI resolved using paths + added via addImportPath() (if importType is LibraryImport). + + The \a prefix may be empty, in which case the import location is considered for + unqualified types. + + The base URL must already have been set with Import::setBaseUrl(). +*/ +bool QQmlImports::addImport(QQmlImportDatabase *importDb, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QQmlScript::Import::Type importType, + const QQmlDirComponents &qmldircomponentsnetwork, + QList<QQmlError> *errors) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: " + << uri << " " << vmaj << '.' << vmin << " " + << (importType==QQmlScript::Import::Library? "Library" : "File") + << " as " << prefix; + + return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix. + The \a prefix must contain the dot. + + \a qmldirPath is the location of the qmldir file. + */ +QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, + const QString &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix) +{ + QStringList searchPaths = filePluginPath; + bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath); + if (!qmldirPluginPathIsRelative) + searchPaths.prepend(qmldirPluginPath); + + foreach (const QString &pluginPath, searchPaths) { + + QString resolvedPath; + if (pluginPath == QLatin1String(".")) { + if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String(".")) + resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + qmldirPluginPath); + else + resolvedPath = qmldirPath; + } else { + if (QDir::isRelativePath(pluginPath)) + resolvedPath = QDir::cleanPath(qmldirPath + QLatin1Char('/') + pluginPath); + else + resolvedPath = pluginPath; + } + + // hack for resources, should probably go away + if (resolvedPath.startsWith(QLatin1Char(':'))) + resolvedPath = QCoreApplication::applicationDirPath(); + + if (!resolvedPath.endsWith(QLatin1Char('/'))) + resolvedPath += QLatin1Char('/'); + + foreach (const QString &suffix, suffixes) { + QString pluginFileName = prefix; + + pluginFileName += baseName; + pluginFileName += suffix; + + QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName); + if (!absolutePath.isEmpty()) + return absolutePath; + } + } + + if (qmlImportTrace()) + qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName + << "in" << qmldirPath; + + return QString(); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a dir and the platform suffix. + + \table + \header \i Platform \i Valid suffixes + \row \i Windows \i \c .dll + \row \i Unix/Linux \i \c .so + \row \i AIX \i \c .a + \row \i HP-UX \i \c .sl, \c .so (HP-UXi) + \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so + \endtable + + Version number on unix are ignored. +*/ +QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, + const QString &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName) +{ +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("d.dll") // try a qmake-style debug build first +# endif + << QLatin1String(".dll")); +#else + +# if defined(Q_OS_DARWIN) + + return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("_debug.dylib") // try a qmake-style debug build first + << QLatin1String(".dylib") +# else + << QLatin1String(".dylib") + << QLatin1String("_debug.dylib") // try a qmake-style debug build after +# endif + << QLatin1String(".so") + << QLatin1String(".bundle"), + QLatin1String("lib")); +# else // Generic Unix + QStringList validSuffixList; + +# if defined(Q_OS_HPUX) +/* + See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": + "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), + the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." + */ + validSuffixList << QLatin1String(".sl"); +# if defined __ia64 + validSuffixList << QLatin1String(".so"); +# endif +# elif defined(Q_OS_AIX) + validSuffixList << QLatin1String(".a") << QLatin1String(".so"); +# elif defined(Q_OS_UNIX) + validSuffixList << QLatin1String(".so"); +# endif + + // Examples of valid library names: + // libfoo.so + + return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); +# endif + +#endif +} + +/*! + \internal +*/ +QStringList QQmlImportDatabase::pluginPathList() const +{ + return filePluginPath; +} + +/*! + \internal +*/ +void QQmlImportDatabase::setPluginPathList(const QStringList &paths) +{ + filePluginPath = paths; +} + +/*! + \internal +*/ +void QQmlImportDatabase::addPluginPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path; + + QUrl url = QUrl(path); + if (url.isRelative() || url.scheme() == QLatin1String("file") + || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path + QDir dir = QDir(path); + filePluginPath.prepend(dir.canonicalPath()); + } else { + filePluginPath.prepend(path); + } +} + +/*! + \internal +*/ +void QQmlImportDatabase::addImportPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path; + + if (path.isEmpty()) + return; + + QUrl url = QUrl(path); + QString cPath; + + if (url.isRelative() || url.scheme() == QLatin1String("file") + || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path + QDir dir = QDir(path); + cPath = dir.canonicalPath(); + } else { + cPath = path; + cPath.replace(QLatin1Char('\\'), QLatin1Char('/')); + } + + if (!cPath.isEmpty() + && !fileImportPath.contains(cPath)) + fileImportPath.prepend(cPath); +} + +/*! + \internal +*/ +QStringList QQmlImportDatabase::importPathList() const +{ + return fileImportPath; +} + +/*! + \internal +*/ +void QQmlImportDatabase::setImportPathList(const QStringList &paths) +{ + fileImportPath = paths; +} + +/*! + \internal +*/ +bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath; + +#ifndef QT_NO_LIBRARY + QFileInfo fileInfo(filePath); + const QString absoluteFilePath = fileInfo.absoluteFilePath(); + + bool engineInitialized = initializedPlugins.contains(absoluteFilePath); + bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); + + if (typesRegistered) { + Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri, + "QQmlImportDatabase::importExtension", + "Internal error: Plugin imported previously with different uri"); + } + + if (!engineInitialized || !typesRegistered) { + if (!QQml_isFileCaseCorrect(absoluteFilePath)) { + if (errors) { + QQmlError error; + error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath)); + errors->prepend(error); + } + return false; + } + QPluginLoader loader(absoluteFilePath); + + if (!loader.load()) { + if (errors) { + QQmlError error; + error.setDescription(loader.errorString()); + errors->prepend(error); + } + return false; + } + + QObject *instance = loader.instance(); + if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) { + + const QByteArray bytes = uri.toUtf8(); + const char *moduleId = bytes.constData(); + if (!typesRegistered) { + + // XXX thread this code should probably be protected with a mutex. + qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); + iface->registerTypes(moduleId); + } + if (!engineInitialized) { + // things on the engine (eg. adding new global objects) have to be done for every + // engine. + // XXX protect against double initialization + initializedPlugins.insert(absoluteFilePath); + + QQmlExtensionInterface *eiface = + qobject_cast<QQmlExtensionInterface *>(instance); + if (eiface) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + ep->typeLoader.initializeEngine(eiface, moduleId); + } + } + } else { + if (errors) { + QQmlError error; + error.setDescription(loader.errorString()); + errors->prepend(error); + } + return false; + } + } + + return true; +#else + return false; +#endif +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h new file mode 100644 index 0000000000..ff19510525 --- /dev/null +++ b/src/qml/qml/qqmlimport_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLIMPORT_P_H +#define QQMLIMPORT_P_H + +#include <QtCore/qurl.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qset.h> +#include <QtCore/qstringlist.h> +#include <private/qqmldirparser_p.h> +#include <private/qqmlscript_p.h> +#include <private/qqmlmetatype_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. +// + +QT_BEGIN_NAMESPACE + +class QQmlTypeNameCache; +class QQmlEngine; +class QDir; +class QQmlImportedNamespace; +class QQmlImportsPrivate; +class QQmlImportDatabase; +class QQmlTypeLoader; + +// Exported for QtQuick1 +class Q_QML_EXPORT QQmlImports +{ +public: + QQmlImports(QQmlTypeLoader *); + QQmlImports(const QQmlImports &); + ~QQmlImports(); + QQmlImports &operator=(const QQmlImports &); + + void setBaseUrl(const QUrl &url, const QString &urlString = QString()); + QUrl baseUrl() const; + + bool resolveType(const QString& type, + QQmlType** type_return, QString* url_return, + int *version_major, int *version_minor, + QQmlImportedNamespace** ns_return, + QList<QQmlError> *errors = 0) const; + bool resolveType(QQmlImportedNamespace*, + const QString& type, + QQmlType** type_return, QString* url_return, + int *version_major, int *version_minor) const; + + bool addImport(QQmlImportDatabase *, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QQmlScript::Import::Type importType, + const QQmlDirComponents &qmldircomponentsnetwork, + QList<QQmlError> *errors); + + void populateCache(QQmlTypeNameCache *cache, QQmlEngine *) const; + + struct ScriptReference + { + QString nameSpace; + QString qualifier; + QUrl location; + }; + + QList<ScriptReference> resolvedScripts() const; + +private: + friend class QQmlImportDatabase; + QQmlImportsPrivate *d; +}; + +class QQmlImportDatabase +{ + Q_DECLARE_TR_FUNCTIONS(QQmlImportDatabase) +public: + QQmlImportDatabase(QQmlEngine *); + ~QQmlImportDatabase(); + + bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); + + QStringList importPathList() const; + void setImportPathList(const QStringList &paths); + void addImportPath(const QString& dir); + + QStringList pluginPathList() const; + void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& path); + +private: + friend class QQmlImportsPrivate; + QString resolvePlugin(QQmlTypeLoader *typeLoader, + const QString &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix = QString()); + QString resolvePlugin(QQmlTypeLoader *typeLoader, + const QString &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName); + + + // XXX thread + QStringList filePluginPath; + QStringList fileImportPath; + + QSet<QString> initializedPlugins; + QQmlEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QQMLIMPORT_P_H + diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp new file mode 100644 index 0000000000..ee622ba048 --- /dev/null +++ b/src/qml/qml/qqmlincubator.cpp @@ -0,0 +1,696 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlincubator.h" +#include "qqmlcomponent.h" +#include "qqmlincubator_p.h" + +#include "qqmlcompiler_p.h" +#include "qqmlexpression_p.h" + +// XXX TODO +// - check that the Component.onCompleted behavior is the same as 4.8 in the synchronous and +// async if nested cases +void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) +{ + QQmlIncubatorPrivate *p = i.d; + + QQmlIncubator::IncubationMode mode = i.incubationMode(); + + if (!incubationController) + mode = QQmlIncubator::Synchronous; + + if (mode == QQmlIncubator::AsynchronousIfNested) { + mode = QQmlIncubator::Synchronous; + + // Need to find the first constructing context and see if it is asynchronous + QQmlIncubatorPrivate *parentIncubator = 0; + QQmlContextData *cctxt = forContext; + while (cctxt) { + if (cctxt->activeVMEData) { + parentIncubator = (QQmlIncubatorPrivate *)cctxt->activeVMEData; + break; + } + cctxt = cctxt->parent; + } + + if (parentIncubator && parentIncubator->isAsynchronous) { + mode = QQmlIncubator::Asynchronous; + p->waitingOnMe = parentIncubator; + parentIncubator->waitingFor.insert(p); + } + } + + p->isAsynchronous = (mode != QQmlIncubator::Synchronous); + + inProgressCreations++; + + if (mode == QQmlIncubator::Synchronous) { + typedef QQmlIncubatorPrivate IP; + QRecursionWatcher<IP, &IP::recursion> watcher(p); + + p->changeStatus(QQmlIncubator::Loading); + + if (!watcher.hasRecursed()) { + QQmlVME::Interrupt i; + p->incubate(i); + } + } else { + incubatorList.insert(p); + incubatorCount++; + + p->vmeGuard.guard(&p->vme); + p->changeStatus(QQmlIncubator::Loading); + + if (incubationController) + incubationController->incubatingObjectCountChanged(incubatorCount); + } +} + +/*! +Sets the engine's incubation \a controller. The engine can only have one active controller +and it does not take ownership of it. + +\sa incubationController() +*/ +void QQmlEngine::setIncubationController(QQmlIncubationController *controller) +{ + Q_D(QQmlEngine); + if (d->incubationController) + d->incubationController->d = 0; + d->incubationController = controller; + if (controller) controller->d = d; +} + +/*! +Returns the currently set incubation controller, or 0 if no controller has been set. + +\sa setIncubationController() +*/ +QQmlIncubationController *QQmlEngine::incubationController() const +{ + Q_D(const QQmlEngine); + return d->incubationController; +} + +QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, + QQmlIncubator::IncubationMode m) +: q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute), + result(0), component(0), vme(this), waitingOnMe(0) +{ +} + +QQmlIncubatorPrivate::~QQmlIncubatorPrivate() +{ +} + +void QQmlIncubatorPrivate::clear() +{ + if (next.isInList()) { + next.remove(); + Q_ASSERT(component); + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(component->engine); + component->release(); + component = 0; + enginePriv->incubatorCount--; + QQmlIncubationController *controller = enginePriv->incubationController; + if (controller) + controller->incubatingObjectCountChanged(enginePriv->incubatorCount); + } else if (component) { + component->release(); + component = 0; + } + if (!rootContext.isNull()) { + rootContext->activeVMEData = 0; + rootContext = 0; + } + + if (nextWaitingFor.isInList()) { + Q_ASSERT(waitingOnMe); + nextWaitingFor.remove(); + waitingOnMe = 0; + } +} + +/*! +\class QQmlIncubationController +\brief QQmlIncubationController instances drive the progress of QQmlIncubators + +In order to behave asynchronously and not introduce stutters or freezes in an application, +the process of creating objects a QQmlIncubators must be driven only during the +application's idle time. QQmlIncubationController allows the application to control +exactly when, how often and for how long this processing occurs. + +A QQmlIncubationController derived instance should be created and set on a +QQmlEngine by calling the QQmlEngine::setIncubationController() method. +Processing is then controlled by calling the QQmlIncubationController::incubateFor() +or QQmlIncubationController::incubateWhile() methods as dictated by the application's +requirements. + +For example, this is an example of a incubation controller that will incubate for a maximum +of 5 milliseconds out of every 16 milliseconds. + +\code +class PeriodicIncubationController : public QObject, + public QQmlIncubationController +{ +public: + PeriodicIncubationController() { + startTimer(16); + } + +protected: + virtual void timerEvent(QTimerEvent *) { + incubateFor(5); + } +}; +\endcode + +Although the previous example would work, it is not optimal. Real world incubation +controllers should try and maximize the amount of idle time they consume - rather +than a static amount like 5 milliseconds - while not disturbing the application. +*/ + +/*! +Create a new incubation controller. +*/ +QQmlIncubationController::QQmlIncubationController() +: d(0) +{ +} + +/*! \internal */ +QQmlIncubationController::~QQmlIncubationController() +{ + if (d) QQmlEnginePrivate::get(d)->setIncubationController(0); + d = 0; +} + +/*! +Return the QQmlEngine this incubation controller is set on, or 0 if it +has not been set on any engine. +*/ +QQmlEngine *QQmlIncubationController::engine() const +{ + return QQmlEnginePrivate::get(d); +} + +/*! +Return the number of objects currently incubating. +*/ +int QQmlIncubationController::incubatingObjectCount() const +{ + if (d) + return d->incubatorCount; + else + return 0; +} + +/*! +Called when the number of incubating objects changes. \a incubatingObjectCount is the +new number of incubating objects. + +The default implementation does nothing. +*/ +void QQmlIncubationController::incubatingObjectCountChanged(int incubatingObjectCount) +{ + Q_UNUSED(incubatingObjectCount); +} + +void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) +{ + if (!component) + return; + typedef QQmlIncubatorPrivate IP; + QRecursionWatcher<IP, &IP::recursion> watcher(this); + + QQmlEngine *engine = component->engine; + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + + bool guardOk = vmeGuard.isOK(); + vmeGuard.clear(); + + if (!guardOk) { + QQmlError error; + error.setUrl(component->url); + error.setDescription(QQmlComponent::tr("Object destroyed during incubation")); + errors << error; + progress = QQmlIncubatorPrivate::Completed; + + goto finishIncubate; + } + + if (progress == QQmlIncubatorPrivate::Execute) { + enginePriv->referenceScarceResources(); + QObject *tresult = vme.execute(&errors, i); + enginePriv->dereferenceScarceResources(); + + if (watcher.hasRecursed()) + return; + + result = tresult; + if (errors.isEmpty() && result == 0) + goto finishIncubate; + + if (result) { + QQmlData *ddata = QQmlData::get(result); + Q_ASSERT(ddata); + ddata->indestructible = true; + + q->setInitialState(result); + } + + if (watcher.hasRecursed()) + return; + + if (errors.isEmpty()) + progress = QQmlIncubatorPrivate::Completing; + else + progress = QQmlIncubatorPrivate::Completed; + + changeStatus(calculateStatus()); + + if (watcher.hasRecursed()) + return; + + if (i.shouldInterrupt()) + goto finishIncubate; + } + + if (progress == QQmlIncubatorPrivate::Completing) { + do { + if (watcher.hasRecursed()) + return; + + QQmlContextData *ctxt = vme.complete(i); + if (ctxt) { + rootContext = ctxt; + progress = QQmlIncubatorPrivate::Completed; + goto finishIncubate; + } + } while (!i.shouldInterrupt()); + } + +finishIncubate: + if (progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) { + typedef QQmlIncubatorPrivate IP; + + QQmlIncubatorPrivate *isWaiting = waitingOnMe; + clear(); + + if (isWaiting) { + QRecursionWatcher<IP, &IP::recursion> watcher(isWaiting); + changeStatus(calculateStatus()); + if (!watcher.hasRecursed()) + isWaiting->incubate(i); + } else { + changeStatus(calculateStatus()); + } + + enginePriv->inProgressCreations--; + + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } else { + vmeGuard.guard(&vme); + } +} + +/*! +Incubate objects for \a msecs, or until there are no more objects to incubate. +*/ +void QQmlIncubationController::incubateFor(int msecs) +{ + if (!d || d->incubatorCount == 0) + return; + + QQmlVME::Interrupt i(msecs * 1000000); + i.reset(); + do { + QQmlIncubatorPrivate *p = (QQmlIncubatorPrivate*)d->incubatorList.first(); + p->incubate(i); + } while (d && d->incubatorCount != 0 && !i.shouldInterrupt()); +} + +/*! +Incubate objects while the bool pointed to by \a flag is true, or until there are no +more objects to incubate. + +Generally this method is used in conjunction with a thread or a UNIX signal that sets +the bool pointed to by \a flag to false when it wants incubation to be interrupted. +*/ +void QQmlIncubationController::incubateWhile(bool *flag) +{ + if (!d || d->incubatorCount == 0) + return; + + QQmlVME::Interrupt i(flag); + do { + QQmlIncubatorPrivate *p = (QQmlIncubatorPrivate*)d->incubatorList.first(); + p->incubate(i); + } while (d && d->incubatorCount != 0 && !i.shouldInterrupt()); +} + +/*! +\class QQmlIncubator +\brief The QQmlIncubator class allows QML objects to be created asynchronously. + +Creating QML objects - like delegates in a view, or a new page in an application - can take +a noticable amount of time, especially on resource constrained mobile devices. When an +application uses QQmlComponent::create() directly, the QML object instance is created +synchronously which, depending on the complexity of the object, can cause noticable pauses or +stutters in the application. + +The use of QQmlIncubator gives more control over the creation of a QML object, +including allowing it to be created asynchronously using application idle time. The following +example shows a simple use of QQmlIncubator. + +\code +QQmlIncubator incubator; +component->create(incubator); + +while (incubator.isReady()) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 50); +} + +QObject *object = incubator.object(); +\endcode + +Asynchronous incubators are controlled by a QQmlIncubationController that is +set on the QQmlEngine, which lets the engine know when the application is idle and +incubating objects should be processed. If an incubation controller is not set on the +QQmlEngine, QQmlIncubator creates objects synchronously regardless of the +specified IncubationMode. + +QQmlIncubator supports three incubation modes: +\list +\i Synchronous The creation occurs synchronously. That is, once the +QQmlComponent::create() call returns, the incubator will already be in either the +Error or Ready state. A synchronous incubator has no real advantage compared to using +the synchronous creation methods on QQmlComponent directly, but it may simplify an +application's implementation to use the same API for both synchronous and asynchronous +creations. + +\i Asynchronous (default) The creation occurs asynchronously, assuming a +QQmlIncubatorController is set on the QQmlEngine. + +The incubator will remain in the Loading state until either the creation is complete or an error +occurs. The statusChanged() callback can be used to be notified of status changes. + +Applications should use the Asynchronous incubation mode to create objects that are not needed +immediately. For example, the ListView element uses Asynchronous incubation to create objects +that are slightly off screen while the list is being scrolled. If, during asynchronous creation, +the object is needed immediately the QQmlIncubator::forceCompletion() method can be called +to complete the creation process synchronously. + +\i AsynchronousIfNested The creation will occur asynchronously if part of a nested asynchronous +creation, or synchronously if not. + +In most scenarios where a QML element or component wants the appearance of a synchronous +instantiation, it should use this mode. + +This mode is best explained with an example. When the ListView element is first created, it needs +to populate itself with an initial set of delegates to show. If the ListView was 400 pixels high, +and each delegate was 100 pixels high, it would need to create four initial delegate instances. If +the ListView used the Asynchronous incubation mode, the ListView would always be created empty and +then, sometime later, the four initial elements would appear. + +Conversely, if the ListView was to use the Synchronous incubation mode it would behave correctly +but it may introduce stutters into the application. As QML would have to stop and instantiate the +ListView's delegates synchronously, if the ListView was part of a QML component that was being +instantiated asynchronously this would undo much of the benefit of asynchronous instantiation. + +The AsynchronousIfNested mode reconciles this problem. By using AsynchronousIfNested, the ListView +delegates are instantiated asynchronously if the ListView itself is already part of an asynchronous +instantiation, and synchronously otherwise. In the case of a nested asynchronous instantiation, the +outer asynchronous instantiation will not complete until after all the nested instantiations have also +completed. This ensures that by the time the outer asynchronous instantitation completes, inner +elements like ListView have already completed loading their initial delegates. + +It is almost always incorrect to use the Synchronous incubation mode - elements or components that +want the appearance of synchronous instantiation, but without the downsides of introducing freezes +or stutters into the application, should use the AsynchronousIfNested incubation mode. +\endlist +*/ + +/*! +Create a new incubator with the specified \a mode +*/ +QQmlIncubator::QQmlIncubator(IncubationMode mode) +: d(new QQmlIncubatorPrivate(this, mode)) +{ +} + +/*! \internal */ +QQmlIncubator::~QQmlIncubator() +{ + clear(); + + delete d; d = 0; +} + +/*! +\enum QQmlIncubator::IncubationMode + +Specifies the mode the incubator operates in. Regardless of the incubation mode, a +QQmlIncubator will behave synchronously if the QQmlEngine does not have +a QQmlIncubationController set. + +\value Asynchronous The object will be created asynchronously. +\value AsynchronousIfNested If the object is being created in a context that is already part +of an asynchronous creation, this incubator will join that existing incubation and execute +asynchronously. The existing incubation will not become Ready until both it and this +incubation have completed. Otherwise, the incubation will execute synchronously. +\value Synchronous The object will be created synchronously. +*/ + +/*! +\enum QQmlIncubator::Status + +Specifies the status of the QQmlIncubator. + +\value Null Incubation is not in progress. Call QQmlComponent::create() to begin incubating. +\value Ready The object is fully created and can be accessed by calling object(). +\value Loading The object is in the process of being created. +\value Error An error occurred. The errors can be access by calling errors(). +*/ + +/*! +Clears the incubator. Any in-progress incubation is aborted. If the incubator is in the +Ready state, the created object is \b not deleted. +*/ +void QQmlIncubator::clear() +{ + typedef QQmlIncubatorPrivate IP; + QRecursionWatcher<IP, &IP::recursion> watcher(d); + + Status s = status(); + + if (s == Null) + return; + + QQmlEnginePrivate *enginePriv = 0; + if (s == Loading) { + Q_ASSERT(d->component); + enginePriv = QQmlEnginePrivate::get(d->component->engine); + if (d->result) d->result->deleteLater(); + d->result = 0; + } + + d->clear(); + + d->vme.reset(); + d->vmeGuard.clear(); + + Q_ASSERT(d->component == 0); + Q_ASSERT(d->waitingOnMe == 0); + Q_ASSERT(d->waitingFor.isEmpty()); + Q_ASSERT(!d->nextWaitingFor.isInList()); + + d->errors.clear(); + d->progress = QQmlIncubatorPrivate::Execute; + d->result = 0; + + if (s == Loading) { + Q_ASSERT(enginePriv); + + enginePriv->inProgressCreations--; + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } + + d->changeStatus(Null); +} + +/*! +Force any in-progress incubation to finish synchronously. Once this call +returns, the incubator will not be in the Loading state. +*/ +void QQmlIncubator::forceCompletion() +{ + QQmlVME::Interrupt i; + while (Loading == status()) { + while (Loading == status() && !d->waitingFor.isEmpty()) + static_cast<QQmlIncubatorPrivate *>(d->waitingFor.first())->incubate(i); + if (Loading == status()) + d->incubate(i); + } +} + +/*! +Returns true if the incubator's status() is Null. +*/ +bool QQmlIncubator::isNull() const +{ + return status() == Null; +} + +/*! +Returns true if the incubator's status() is Ready. +*/ +bool QQmlIncubator::isReady() const +{ + return status() == Ready; +} + +/*! +Returns true if the incubator's status() is Error. +*/ +bool QQmlIncubator::isError() const +{ + return status() == Error; +} + +/*! +Returns true if the incubator's status() is Loading. +*/ +bool QQmlIncubator::isLoading() const +{ + return status() == Loading; +} + +/*! +Return the list of errors encountered while incubating the object. +*/ +QList<QQmlError> QQmlIncubator::errors() const +{ + return d->errors; +} + +/*! +Return the incubation mode passed to the QQmlIncubator constructor. +*/ +QQmlIncubator::IncubationMode QQmlIncubator::incubationMode() const +{ + return d->mode; +} + +/*! +Return the current status of the incubator. +*/ +QQmlIncubator::Status QQmlIncubator::status() const +{ + return d->status; +} + +/*! +Return the incubated object if the status is Ready, otherwise 0. +*/ +QObject *QQmlIncubator::object() const +{ + if (status() != Ready) return 0; + else return d->result; +} + +/*! +Called when the status of the incubator changes. \a status is the new status. + +The default implementation does nothing. +*/ +void QQmlIncubator::statusChanged(Status status) +{ + Q_UNUSED(status); +} + +/*! +Called after the object is first created, but before property bindings are +evaluated and, if applicable, QQmlParserStatus::componentComplete() is +called. This is equivalent to the point between QQmlComponent::beginCreate() +and QQmlComponent::endCreate(), and can be used to assign initial values +to the object's properties. + +The default implementation does nothing. +*/ +void QQmlIncubator::setInitialState(QObject *object) +{ + Q_UNUSED(object); +} + +void QQmlIncubatorPrivate::changeStatus(QQmlIncubator::Status s) +{ + if (s == status) + return; + + status = s; + q->statusChanged(status); +} + +QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const +{ + if (!errors.isEmpty()) + return QQmlIncubator::Error; + else if (result && progress == QQmlIncubatorPrivate::Completed && + waitingFor.isEmpty()) + return QQmlIncubator::Ready; + else if (component) + return QQmlIncubator::Loading; + else + return QQmlIncubator::Null; +} + diff --git a/src/qml/qml/qqmlincubator.h b/src/qml/qml/qqmlincubator.h new file mode 100644 index 0000000000..5d8ae7d6c3 --- /dev/null +++ b/src/qml/qml/qqmlincubator.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINCUBATOR_H +#define QQMLINCUBATOR_H + +#include <QtQml/qqmlerror.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlEngine; + +class QQmlIncubatorPrivate; +class Q_QML_EXPORT QQmlIncubator +{ + Q_DISABLE_COPY(QQmlIncubator) +public: + enum IncubationMode { + Asynchronous, + AsynchronousIfNested, + Synchronous + }; + enum Status { + Null, + Ready, + Loading, + Error + }; + + QQmlIncubator(IncubationMode = Asynchronous); + virtual ~QQmlIncubator(); + + void clear(); + void forceCompletion(); + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + QList<QQmlError> errors() const; + + IncubationMode incubationMode() const; + + Status status() const; + + QObject *object() const; + +protected: + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + +private: + friend class QQmlComponent; + friend class QQmlEnginePrivate; + friend class QQmlIncubatorPrivate; + QQmlIncubatorPrivate *d; +}; + +class QQmlEnginePrivate; +class Q_QML_EXPORT QQmlIncubationController +{ + Q_DISABLE_COPY(QQmlIncubationController) +public: + QQmlIncubationController(); + virtual ~QQmlIncubationController(); + + QQmlEngine *engine() const; + int incubatingObjectCount() const; + + void incubateFor(int msecs); + void incubateWhile(bool *flag); + +protected: + virtual void incubatingObjectCountChanged(int); + +private: + friend class QQmlEngine; + friend class QQmlEnginePrivate; + friend class QQmlIncubatorPrivate; + QQmlEnginePrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLINCUBATOR_H diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h new file mode 100644 index 0000000000..0dec34a8d5 --- /dev/null +++ b/src/qml/qml/qqmlincubator_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINCUBATOR_P_H +#define QQMLINCUBATOR_P_H + +#include <private/qintrusivelist_p.h> +#include <private/qqmlvme_p.h> +#include <private/qrecursionwatcher_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_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. +// + +QT_BEGIN_NAMESPACE + +class QQmlCompiledData; +class QQmlIncubator; +class QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator +{ +public: + QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m); + ~QQmlIncubatorPrivate(); + + QQmlIncubator *q; + + QQmlIncubator::Status calculateStatus() const; + void changeStatus(QQmlIncubator::Status); + QQmlIncubator::Status status; + + QQmlIncubator::IncubationMode mode; + bool isAsynchronous; + + QList<QQmlError> errors; + + enum Progress { Execute, Completing, Completed }; + Progress progress; + + QQmlGuard<QObject> result; + QQmlGuardedContextData rootContext; + QQmlCompiledData *component; + QQmlVME vme; + QQmlVMEGuard vmeGuard; + + QQmlIncubatorPrivate *waitingOnMe; + typedef QQmlEnginePrivate::Incubator QIPBase; + QIntrusiveList<QIPBase, &QIPBase::nextWaitingFor> waitingFor; + + QRecursionNode recursion; + + void clear(); + + void incubate(QQmlVME::Interrupt &i); +}; + +QT_END_NAMESPACE + +#endif // QQMLINCUBATOR_P_H + diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp new file mode 100644 index 0000000000..53145737d8 --- /dev/null +++ b/src/qml/qml/qqmlinfo.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlinfo.h" + +#include "qqmldata_p.h" +#include "qqmlcontext.h" +#include "qqmlcontext_p.h" +#include "qqmlmetatype_p.h" +#include "qqmlengine_p.h" + +#include <QCoreApplication> + +QT_BEGIN_NAMESPACE + +/*! + \fn QQmlInfo qmlInfo(const QObject *object) + \relates QQmlEngine + + Prints warning messages that include the file and line number for the + specified QML \a object. + + When QML types display warning messages, it improves traceability + if they include the QML file and line number on which the + particular instance was instantiated. + + To include the file and line number, an object must be passed. If + the file and line number is not available for that instance + (either it was not instantiated by the QML engine or location + information is disabled), "unknown location" will be used instead. + + For example, + + \code + qmlInfo(object) << tr("component property is a write-once property"); + \endcode + + prints + + \code + QML MyCustomType (unknown location): component property is a write-once property + \endcode +*/ + +class QQmlInfoPrivate +{ +public: + QQmlInfoPrivate() : ref (1), object(0) {} + + int ref; + const QObject *object; + QString buffer; + QList<QQmlError> errors; +}; + +QQmlInfo::QQmlInfo(QQmlInfoPrivate *p) +: QDebug(&p->buffer), d(p) +{ + nospace(); +} + +QQmlInfo::QQmlInfo(const QQmlInfo &other) +: QDebug(other), d(other.d) +{ + d->ref++; +} + +QQmlInfo::~QQmlInfo() +{ + if (0 == --d->ref) { + QList<QQmlError> errors = d->errors; + + QQmlEngine *engine = 0; + + if (!d->buffer.isEmpty()) { + QQmlError error; + + QObject *object = const_cast<QObject *>(d->object); + + if (object) { + engine = qmlEngine(d->object); + QString typeName; + QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); + if (type) { + typeName = type->qmlTypeName(); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash+1); + } else { + typeName = QString::fromUtf8(object->metaObject()->className()); + int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); + if (marker != -1) + typeName = typeName.left(marker); + + marker = typeName.indexOf(QLatin1String("_QML_")); + if (marker != -1) { + typeName = typeName.left(marker); + typeName += QLatin1Char('*'); + type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); + if (type) { + typeName = type->qmlTypeName(); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash+1); + } + } + } + + d->buffer.prepend(QLatin1String("QML ") + typeName + QLatin1String(": ")); + + QQmlData *ddata = QQmlData::get(object, false); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) { + error.setUrl(ddata->outerContext->url); + error.setLine(ddata->lineNumber); + error.setColumn(ddata->columnNumber); + } + } + + error.setDescription(d->buffer); + + errors.prepend(error); + } + + QQmlEnginePrivate::warning(engine, errors); + + delete d; + } +} + +QQmlInfo qmlInfo(const QObject *me) +{ + QQmlInfoPrivate *d = new QQmlInfoPrivate; + d->object = me; + return QQmlInfo(d); +} + +QQmlInfo qmlInfo(const QObject *me, const QQmlError &error) +{ + QQmlInfoPrivate *d = new QQmlInfoPrivate; + d->object = me; + d->errors << error; + return QQmlInfo(d); +} + +QQmlInfo qmlInfo(const QObject *me, const QList<QQmlError> &errors) +{ + QQmlInfoPrivate *d = new QQmlInfoPrivate; + d->object = me; + d->errors = errors; + return QQmlInfo(d); +} + + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlinfo.h b/src/qml/qml/qqmlinfo.h new file mode 100644 index 0000000000..92d6d72e8b --- /dev/null +++ b/src/qml/qml/qqmlinfo.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINFO_H +#define QQMLINFO_H + +#include <QtCore/qdebug.h> +#include <QtCore/qurl.h> +#include <QtQml/qqmlerror.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlInfoPrivate; +class Q_QML_EXPORT QQmlInfo : public QDebug +{ +public: + QQmlInfo(const QQmlInfo &); + ~QQmlInfo(); + + inline QQmlInfo &operator<<(QChar t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(bool t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(char t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(signed short t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(unsigned short t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(signed int t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(unsigned int t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(signed long t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(unsigned long t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(qint64 t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(quint64 t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(float t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(double t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(const char* t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(const QString & t) { QDebug::operator<<(t.toLocal8Bit().constData()); return *this; } + inline QQmlInfo &operator<<(const QStringRef & t) { return operator<<(t.toString()); } + inline QQmlInfo &operator<<(const QLatin1String &t) { QDebug::operator<<(t.latin1()); return *this; } + inline QQmlInfo &operator<<(const QByteArray & t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(const void * t) { QDebug::operator<<(t); return *this; } + inline QQmlInfo &operator<<(QTextStreamFunction f) { QDebug::operator<<(f); return *this; } + inline QQmlInfo &operator<<(QTextStreamManipulator m) { QDebug::operator<<(m); return *this; } +#ifndef QT_NO_DEBUG_STREAM + inline QQmlInfo &operator<<(const QUrl &t) { static_cast<QDebug &>(*this) << t; return *this; } +#endif + +private: + friend Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me); + friend Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me, const QQmlError &error); + friend Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me, const QList<QQmlError> &errors); + + QQmlInfo(QQmlInfoPrivate *); + QQmlInfoPrivate *d; +}; + +Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me); +Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me, const QQmlError &error); +Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me, const QList<QQmlError> &errors); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLINFO_H diff --git a/src/qml/qml/qqmlinstruction.cpp b/src/qml/qml/qqmlinstruction.cpp new file mode 100644 index 0000000000..72f04c9d61 --- /dev/null +++ b/src/qml/qml/qqmlinstruction.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlinstruction_p.h" + +#include "qqmlcompiler_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +void QQmlCompiledData::dump(QQmlInstruction *instr, int idx) +{ +#ifdef QT_NO_DEBUG_STREAM + Q_UNUSED(instr) + Q_UNUSED(idx) +#else + switch (instructionType(instr)) { + case QQmlInstruction::Init: + qWarning().nospace() << idx << "\t\t" << "INIT\t\t\t" << instr->init.bindingsSize << "\t" << instr->init.parserStatusSize << "\t" << instr->init.contextCache << "\t" << instr->init.compiledBinding; + break; + case QQmlInstruction::DeferInit: + qWarning().nospace() << idx << "\t\t" << "DEFER_INIT\t\t" << instr->deferInit.bindingsSize << "\t" << instr->deferInit.parserStatusSize; + break; + case QQmlInstruction::Done: + qWarning().nospace() << idx << "\t\t" << "DONE"; + break; + case QQmlInstruction::CreateCppObject: + qWarning().nospace() << idx << "\t\t" << "CREATECPP\t\t\t" << instr->create.type << "\t\t\t" << types.at(instr->create.type).className; + break; + case QQmlInstruction::CreateQMLObject: + qWarning().nospace() << idx << "\t\t" << "CREATEQML\t\t\t" << instr->createQml.type << "\t" << instr->createQml.bindingBits << "\t\t" << types.at(instr->createQml.type).className; + break; + case QQmlInstruction::CompleteQMLObject: + qWarning().nospace() << idx << "\t\t" << "COMPLETEQML"; + break; + case QQmlInstruction::CreateSimpleObject: + qWarning().nospace() << idx << "\t\t" << "CREATE_SIMPLE\t\t" << instr->createSimple.typeSize; + break; + case QQmlInstruction::SetId: + qWarning().nospace() << idx << "\t\t" << "SETID\t\t\t" << instr->setId.value << "\t\t\t" << primitives.at(instr->setId.value); + break; + case QQmlInstruction::SetDefault: + qWarning().nospace() << idx << "\t\t" << "SET_DEFAULT"; + break; + case QQmlInstruction::CreateComponent: + qWarning().nospace() << idx << "\t\t" << "CREATE_COMPONENT\t" << instr->createComponent.count; + break; + case QQmlInstruction::StoreMetaObject: + qWarning().nospace() << idx << "\t\t" << "STORE_META\t\t" << instr->storeMeta.data; + break; + case QQmlInstruction::StoreFloat: + qWarning().nospace() << idx << "\t\t" << "STORE_FLOAT\t\t" << instr->storeFloat.propertyIndex << "\t" << instr->storeFloat.value; + break; + case QQmlInstruction::StoreDouble: + qWarning().nospace() << idx << "\t\t" << "STORE_DOUBLE\t\t" << instr->storeDouble.propertyIndex << "\t" << instr->storeDouble.value; + break; + case QQmlInstruction::StoreDoubleQList: + qWarning().nospace() << idx << "\t\t" << "STORE_DOUBLE_QLIST\t\t" << instr->storeDouble.propertyIndex << "\t" << instr->storeDouble.value; + break; + case QQmlInstruction::StoreInteger: + qWarning().nospace() << idx << "\t\t" << "STORE_INTEGER\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QQmlInstruction::StoreIntegerQList: + qWarning().nospace() << idx << "\t\t" << "STORE_INTEGER_QLIST\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QQmlInstruction::StoreBool: + qWarning().nospace() << idx << "\t\t" << "STORE_BOOL\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QQmlInstruction::StoreBoolQList: + qWarning().nospace() << idx << "\t\t" << "STORE_BOOL_QLIST\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QQmlInstruction::StoreString: + qWarning().nospace() << idx << "\t\t" << "STORE_STRING\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QQmlInstruction::StoreStringList: + qWarning().nospace() << idx << "\t\t" << "STORE_STRINGLIST\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QQmlInstruction::StoreStringQList: + qWarning().nospace() << idx << "\t\t" << "STORE_STRING_QLIST\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QQmlInstruction::StoreTrString: + qWarning().nospace() << idx << "\t\t" << "STORE_TR_STRING\t" << instr->storeTrString.propertyIndex << "\t" << instr->storeTrString.context << "\t" << instr->storeTrString.text << "\t" << instr->storeTrString.comment << "\t" << instr->storeTrString.n; + break; + case QQmlInstruction::StoreTrIdString: + qWarning().nospace() << idx << "\t\t" << "STORE_TRID_STRING\t" << instr->storeTrIdString.propertyIndex << "\t" << instr->storeTrIdString.text << "\t" << instr->storeTrIdString.n; + break; + case QQmlInstruction::StoreByteArray: + qWarning().nospace() << idx << "\t\t" << "STORE_BYTEARRAY" << instr->storeByteArray.propertyIndex << "\t" << instr->storeByteArray.value << "\t\t" << datas.at(instr->storeByteArray.value); + break; + case QQmlInstruction::StoreUrl: + qWarning().nospace() << idx << "\t\t" << "STORE_URL\t\t" << instr->storeUrl.propertyIndex << "\t" << instr->storeUrl.value << "\t\t" << urls.at(instr->storeUrl.value); + break; + case QQmlInstruction::StoreUrlQList: + qWarning().nospace() << idx << "\t\t" << "STORE_URL_QLIST\t\t" << instr->storeUrl.propertyIndex << "\t" << instr->storeUrl.value << "\t\t" << urls.at(instr->storeUrl.value); + break; + case QQmlInstruction::StoreColor: + qWarning().nospace() << idx << "\t\t" << "STORE_COLOR\t\t" << instr->storeColor.propertyIndex << "\t\t\t" << QString::number(instr->storeColor.value, 16); + break; + case QQmlInstruction::StoreDate: + qWarning().nospace() << idx << "\t\t" << "STORE_DATE\t\t" << instr->storeDate.propertyIndex << "\t" << instr->storeDate.value; + break; + case QQmlInstruction::StoreTime: + qWarning().nospace() << idx << "\t\t" << "STORE_TIME\t\t" << instr->storeTime.propertyIndex; + break; + case QQmlInstruction::StoreDateTime: + qWarning().nospace() << idx << "\t\t" << "STORE_DATETIME\t\t" << instr->storeDateTime.propertyIndex; + break; + case QQmlInstruction::StorePoint: + qWarning().nospace() << idx << "\t\t" << "STORE_POINT\t\t" << instr->storePoint.propertyIndex << "\t" << instr->storePoint.point.xp << "\t" << instr->storePoint.point.yp; + break; + case QQmlInstruction::StorePointF: + qWarning().nospace() << idx << "\t\t" << "STORE_POINTF\t\t" << instr->storePointF.propertyIndex << "\t" << instr->storePointF.point.xp << "\t" << instr->storePointF.point.yp; + break; + case QQmlInstruction::StoreSize: + qWarning().nospace() << idx << "\t\t" << "STORE_SIZE\t\t" << instr->storeSize.propertyIndex << "\t" << instr->storeSize.size.wd << "\t" << instr->storeSize.size.ht; + break; + case QQmlInstruction::StoreSizeF: + qWarning().nospace() << idx << "\t\t" << "STORE_SIZEF\t\t" << instr->storeSizeF.propertyIndex << "\t" << instr->storeSizeF.size.wd << "\t" << instr->storeSizeF.size.ht; + break; + case QQmlInstruction::StoreRect: + qWarning().nospace() << idx << "\t\t" << "STORE_RECT\t\t" << instr->storeRect.propertyIndex << "\t" << instr->storeRect.rect.x1 << "\t" << instr->storeRect.rect.y1 << "\t" << instr->storeRect.rect.x2 << "\t" << instr->storeRect.rect.y2; + break; + case QQmlInstruction::StoreRectF: + qWarning().nospace() << idx << "\t\t" << "STORE_RECTF\t\t" << instr->storeRectF.propertyIndex << "\t" << instr->storeRectF.rect.xp << "\t" << instr->storeRectF.rect.yp << "\t" << instr->storeRectF.rect.w << "\t" << instr->storeRectF.rect.h; + break; + case QQmlInstruction::StoreVector3D: + qWarning().nospace() << idx << "\t\t" << "STORE_VECTOR3D\t\t" << instr->storeVector3D.propertyIndex << "\t" << instr->storeVector3D.vector.xp << "\t" << instr->storeVector3D.vector.yp << "\t" << instr->storeVector3D.vector.zp; + break; + case QQmlInstruction::StoreVector4D: + qWarning().nospace() << idx << "\t\t" << "STORE_VECTOR4D\t\t" << instr->storeVector4D.propertyIndex << "\t" << instr->storeVector4D.vector.xp << "\t" << instr->storeVector4D.vector.yp << "\t" << instr->storeVector4D.vector.zp << "\t" << instr->storeVector4D.vector.wp; + break; + case QQmlInstruction::StoreVariant: + qWarning().nospace() << idx << "\t\t" << "STORE_VARIANT\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QQmlInstruction::StoreVariantInteger: + qWarning().nospace() << idx << "\t\t" << "STORE_VARIANT_INTEGER\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QQmlInstruction::StoreVariantDouble: + qWarning().nospace() << idx << "\t\t" << "STORE_VARIANT_DOUBLE\t\t" << instr->storeDouble.propertyIndex << "\t" << instr->storeDouble.value; + break; + case QQmlInstruction::StoreVariantBool: + qWarning().nospace() << idx << "\t\t" << "STORE_VARIANT_BOOL\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QQmlInstruction::StoreObject: + qWarning().nospace() << idx << "\t\t" << "STORE_OBJECT\t\t" << instr->storeObject.propertyIndex; + break; + case QQmlInstruction::StoreVariantObject: + qWarning().nospace() << idx << "\t\t" << "STORE_VARIANT_OBJECT\t" << instr->storeObject.propertyIndex; + break; + case QQmlInstruction::StoreInterface: + qWarning().nospace() << idx << "\t\t" << "STORE_INTERFACE\t\t" << instr->storeObject.propertyIndex; + break; + case QQmlInstruction::StoreSignal: + qWarning().nospace() << idx << "\t\t" << "STORE_SIGNAL\t\t" << instr->storeSignal.signalIndex << "\t" << instr->storeSignal.value << "\t\t" << primitives.at(instr->storeSignal.value); + break; + case QQmlInstruction::StoreImportedScript: + qWarning().nospace() << idx << "\t\t" << "STORE_IMPORTED_SCRIPT\t" << instr->storeScript.value; + break; + case QQmlInstruction::StoreScriptString: + qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope << "\t" << instr->storeScriptString.bindingId; + break; + case QQmlInstruction::AssignSignalObject: + qWarning().nospace() << idx << "\t\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << primitives.at(instr->assignSignalObject.signal); + break; + case QQmlInstruction::AssignCustomType: + qWarning().nospace() << idx << "\t\t" << "ASSIGN_CUSTOMTYPE\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.primitive << "\t" << instr->assignCustomType.type; + break; + case QQmlInstruction::InitV8Bindings: + qWarning().nospace() << idx << "\t\t" << "INIT_V8_BINDING\t" << instr->initV8Bindings.programIndex << "\t" << instr->initV8Bindings.line; + break; + case QQmlInstruction::StoreBinding: + qWarning().nospace() << idx << "\t\t" << "STORE_BINDING\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QQmlInstruction::StoreBindingOnAlias: + qWarning().nospace() << idx << "\t\t" << "STORE_BINDING_ALIAS\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QQmlInstruction::StoreV4Binding: + qWarning().nospace() << idx << "\t\t" << "STORE_COMPILED_BINDING\t" << instr->assignV4Binding.property << "\t" << instr->assignV4Binding.value << "\t" << instr->assignV4Binding.context; + break; + case QQmlInstruction::StoreV8Binding: + qWarning().nospace() << idx << "\t\t" << "STORE_V8_BINDING\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QQmlInstruction::StoreValueSource: + qWarning().nospace() << idx << "\t\t" << "STORE_VALUE_SOURCE\t" << instr->assignValueSource.property.coreIndex << "\t" << instr->assignValueSource.castValue; + break; + case QQmlInstruction::StoreValueInterceptor: + qWarning().nospace() << idx << "\t\t" << "STORE_VALUE_INTERCEPTOR\t" << instr->assignValueInterceptor.property.coreIndex << "\t" << instr->assignValueInterceptor.castValue; + break; + case QQmlInstruction::BeginObject: + qWarning().nospace() << idx << "\t\t" << "BEGIN\t\t\t" << instr->begin.castValue; + break; + case QQmlInstruction::StoreObjectQList: + qWarning().nospace() << idx << "\t\t" << "STORE_OBJECT_QLIST"; + break; + case QQmlInstruction::AssignObjectList: + qWarning().nospace() << idx << "\t\t" << "ASSIGN_OBJECT_LIST"; + break; + case QQmlInstruction::FetchAttached: + qWarning().nospace() << idx << "\t\t" << "FETCH_ATTACHED\t\t" << instr->fetchAttached.id; + break; + case QQmlInstruction::FetchQList: + qWarning().nospace() << idx << "\t\t" << "FETCH_QLIST\t\t" << instr->fetch.property; + break; + case QQmlInstruction::FetchObject: + qWarning().nospace() << idx << "\t\t" << "FETCH\t\t\t" << instr->fetch.property; + break; + case QQmlInstruction::FetchValueType: + qWarning().nospace() << idx << "\t\t" << "FETCH_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type << "\t" << instr->fetchValue.bindingSkipList; + break; + case QQmlInstruction::PopFetchedObject: + qWarning().nospace() << idx << "\t\t" << "POP"; + break; + case QQmlInstruction::PopQList: + qWarning().nospace() << idx << "\t\t" << "POP_QLIST"; + break; + case QQmlInstruction::PopValueType: + qWarning().nospace() << idx << "\t\t" << "POP_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type; + break; + case QQmlInstruction::Defer: + qWarning().nospace() << idx << "\t\t" << "DEFER" << "\t\t\t" << instr->defer.deferCount; + break; + default: + qWarning().nospace() << idx << "\t\t" << "XXX UNKNOWN INSTRUCTION" << "\t" << instructionType(instr); + break; + } +#endif // QT_NO_DEBUG_STREAM +} + +int QQmlInstruction::size(Type type) +{ +#define QML_RETURN_INSTR_SIZE(I, FMT) case I: return QQmlInstructionMeta<(int)I>::Size; + switch (type) { + FOR_EACH_QML_INSTR(QML_RETURN_INSTR_SIZE) + default: return 0; + } +#undef QML_RETURN_INSTR_SIZE +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlinstruction_p.h b/src/qml/qml/qqmlinstruction_p.h new file mode 100644 index 0000000000..b7533aca68 --- /dev/null +++ b/src/qml/qml/qqmlinstruction_p.h @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINSTRUCTION_P_H +#define QQMLINSTRUCTION_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 <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_QML_INSTR(F) \ + F(Init, init) \ + F(DeferInit, deferInit) \ + F(Done, common) \ + F(CreateCppObject, create) \ + F(CreateQMLObject, createQml) \ + F(CompleteQMLObject, completeQml) \ + F(CreateSimpleObject, createSimple) \ + F(SetId, setId) \ + F(SetDefault, common) \ + F(CreateComponent, createComponent) \ + F(StoreMetaObject, storeMeta) \ + F(StoreVariant, storeString) \ + F(StoreVariantInteger, storeInteger) \ + F(StoreVariantDouble, storeDouble) \ + F(StoreVariantBool, storeBool) \ + F(StoreVar, storeString) \ + F(StoreVarInteger, storeInteger) \ + F(StoreVarDouble, storeDouble) \ + F(StoreVarBool, storeBool) \ + F(StoreString, storeString) \ + F(StoreStringList, storeString) \ + F(StoreStringQList, storeString) \ + F(StoreTrString, storeTrString) \ + F(StoreTrIdString, storeTrIdString) \ + F(StoreByteArray, storeByteArray) \ + F(StoreUrl, storeUrl) \ + F(StoreUrlQList, storeUrl) \ + F(StoreFloat, storeFloat) \ + F(StoreDouble, storeDouble) \ + F(StoreDoubleQList, storeDouble) \ + F(StoreBool, storeBool) \ + F(StoreBoolQList, storeBool) \ + F(StoreInteger, storeInteger) \ + F(StoreIntegerQList, storeInteger) \ + F(StoreColor, storeColor) \ + F(StoreDate, storeDate) \ + F(StoreTime, storeTime) \ + F(StoreDateTime, storeDateTime) \ + F(StorePoint, storePoint) \ + F(StorePointF, storePointF) \ + F(StoreSize, storeSize) \ + F(StoreSizeF, storeSizeF) \ + F(StoreRect, storeRect) \ + F(StoreRectF, storeRectF) \ + F(StoreVector3D, storeVector3D) \ + F(StoreVector4D, storeVector4D) \ + F(StoreObject, storeObject) \ + F(AssignCustomType, assignCustomType) \ + F(AssignSignalObject, assignSignalObject) \ + F(StoreSignal, storeSignal) \ + F(StoreImportedScript, storeScript) \ + F(StoreScriptString, storeScriptString) \ + F(BeginObject, begin) \ + F(InitV8Bindings, initV8Bindings) \ + F(StoreBinding, assignBinding) \ + F(StoreBindingOnAlias, assignBinding) \ + F(StoreV8Binding, assignBinding) \ + F(StoreV4Binding, assignV4Binding) \ + F(StoreValueSource, assignValueSource) \ + F(StoreValueInterceptor, assignValueInterceptor) \ + F(StoreObjectQList, common) \ + F(AssignObjectList, assignObjectList) \ + F(StoreVariantObject, storeObject) \ + F(StoreVarObject, storeObject) \ + F(StoreInterface, storeObject) \ + F(FetchAttached, fetchAttached) \ + F(FetchQList, fetchQmlList) \ + F(FetchObject, fetch) \ + F(PopQList, common) \ + F(Defer, defer) \ + F(PopFetchedObject, common) \ + F(FetchValueType, fetchValue) \ + F(PopValueType, fetchValue) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define QML_THREADED_VME_INTERPRETER +#endif + +#ifdef Q_ALIGNOF +# define QML_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlInstruction) - 1) +#else +# define QML_INSTR_ALIGN_MASK (sizeof(void *) - 1) +#endif + +#ifdef QML_THREADED_VME_INTERPRETER +# define QML_INSTR_HEADER void *code; +#else +# define QML_INSTR_HEADER quint8 instructionType; +#endif + +#define QML_INSTR_ENUM(I, FMT) I, +#define QML_INSTR_SIZE(I, FMT) ((sizeof(QQmlInstruction::instr_##FMT) + QML_INSTR_ALIGN_MASK) & ~QML_INSTR_ALIGN_MASK) + +class QQmlCompiledData; +union QQmlInstruction +{ + enum Type { + FOR_EACH_QML_INSTR(QML_INSTR_ENUM) + }; + + struct instr_common { + QML_INSTR_HEADER + }; + struct instr_init { + QML_INSTR_HEADER + int bindingsSize; + int parserStatusSize; + int contextCache; + int compiledBinding; + int objectStackSize; + int listStackSize; + }; + struct instr_deferInit { + QML_INSTR_HEADER + int bindingsSize; + int parserStatusSize; + int objectStackSize; + int listStackSize; + }; + struct instr_createQml { + QML_INSTR_HEADER + int type; + int bindingBits; + bool isRoot; + }; + struct instr_completeQml { + QML_INSTR_HEADER + ushort column; + ushort line; + bool isRoot; + }; + struct instr_create { + QML_INSTR_HEADER + int type; + int data; + ushort column; + ushort line; + bool isRoot; + }; + struct instr_createSimple { + QML_INSTR_HEADER + void (*create)(void *); + int typeSize; + int type; + ushort column; + ushort line; + }; + struct instr_storeMeta { + QML_INSTR_HEADER + int data; + int aliasData; + int propertyCache; + }; + struct instr_setId { + QML_INSTR_HEADER + int value; + int index; + }; + struct instr_assignValueSource { + QML_INSTR_HEADER + QQmlPropertyRawData property; + int owner; + int castValue; + }; + struct instr_assignValueInterceptor { + QML_INSTR_HEADER + QQmlPropertyRawData property; + int owner; + int castValue; + }; + struct instr_initV8Bindings { + QML_INSTR_HEADER + ushort programIndex; + ushort line; + }; + struct instr_assignV4Binding { + QML_INSTR_HEADER + unsigned int property; + int value; + short context; + short owner; + bool isRoot; + ushort line; + ushort column; + }; + struct instr_assignBinding { + QML_INSTR_HEADER + QQmlPropertyRawData property; + int value; + short context; + short owner; + bool isRoot; + ushort line; + ushort column; + }; + struct instr_fetch { + QML_INSTR_HEADER + int property; + ushort line; + }; + struct instr_fetchValue { + QML_INSTR_HEADER + int property; + int type; + quint32 bindingSkipList; + }; + struct instr_fetchQmlList { + QML_INSTR_HEADER + int property; + int type; + }; + struct instr_begin { + QML_INSTR_HEADER + int castValue; + }; + struct instr_storeFloat { + QML_INSTR_HEADER + int propertyIndex; + float value; + }; + struct instr_storeDouble { + QML_INSTR_HEADER + int propertyIndex; + double value; + }; + struct instr_storeInteger { + QML_INSTR_HEADER + int propertyIndex; + int value; + }; + struct instr_storeBool { + QML_INSTR_HEADER + int propertyIndex; + bool value; + }; + struct instr_storeString { + QML_INSTR_HEADER + int propertyIndex; + int value; + }; + struct instr_storeTrString { + QML_INSTR_HEADER + int propertyIndex; + int context; + int text; + int comment; + int n; + }; + struct instr_storeTrIdString { + QML_INSTR_HEADER + int propertyIndex; + int text; + int n; + }; + struct instr_storeByteArray { + QML_INSTR_HEADER + int propertyIndex; + int value; + }; + struct instr_storeScriptString { + QML_INSTR_HEADER + int propertyIndex; + int value; + int scope; + int bindingId; + ushort line; + ushort column; + }; + struct instr_storeScript { + QML_INSTR_HEADER + int value; + }; + struct instr_storeUrl { + QML_INSTR_HEADER + int propertyIndex; + int value; + }; + struct instr_storeColor { + QML_INSTR_HEADER + int propertyIndex; + unsigned int value; + }; + struct instr_storeDate { + QML_INSTR_HEADER + int propertyIndex; + int value; + }; + struct instr_storeTime { + QML_INSTR_HEADER + int propertyIndex; + struct QTime { + int mds; +#if defined(Q_OS_WINCE) + int startTick; +#endif + } time; + }; + struct instr_storeDateTime { + QML_INSTR_HEADER + int propertyIndex; + int date; + instr_storeTime::QTime time; + }; + struct instr_storeRect { + QML_INSTR_HEADER + int propertyIndex; + struct QRect { + int x1; + int y1; + int x2; + int y2; + } rect; + }; + struct instr_storeRectF { + QML_INSTR_HEADER + int propertyIndex; + struct QRectF { + qreal xp; + qreal yp; + qreal w; + qreal h; + } rect; + }; + struct instr_storeObject { + QML_INSTR_HEADER + int propertyIndex; + ushort line; + }; + struct instr_assignCustomType { + QML_INSTR_HEADER + int propertyIndex; + int primitive; + int type; + ushort line; + }; + struct instr_storeSignal { + QML_INSTR_HEADER + int signalIndex; + int value; + short context; + ushort line; + ushort column; + }; + struct instr_assignSignalObject { + QML_INSTR_HEADER + int signal; + ushort line; + }; + struct instr_createComponent { + QML_INSTR_HEADER + int count; + int endLine; + int metaObject; + ushort column; + ushort line; + bool isRoot; + }; + struct instr_fetchAttached { + QML_INSTR_HEADER + int id; + ushort line; + }; + struct instr_defer { + QML_INSTR_HEADER + int deferCount; + }; + struct instr_assignObjectList { + QML_INSTR_HEADER + ushort line; + }; + struct instr_storePoint { + QML_INSTR_HEADER + int propertyIndex; + struct QPoint { + int xp; + int yp; + } point; + }; + struct instr_storePointF { + QML_INSTR_HEADER + int propertyIndex; + struct QPointF { + qreal xp; + qreal yp; + } point; + }; + struct instr_storeSize { + QML_INSTR_HEADER + int propertyIndex; + struct QSize { + int wd; + int ht; + } size; + }; + struct instr_storeSizeF { + QML_INSTR_HEADER + int propertyIndex; + struct QSizeF { + qreal wd; + qreal ht; + } size; + }; + struct instr_storeVector3D { + QML_INSTR_HEADER + int propertyIndex; + struct QVector3D { + float xp; + float yp; + float zp; + } vector; + }; + struct instr_storeVector4D { + QML_INSTR_HEADER + int propertyIndex; + struct QVector4D { + float xp; + float yp; + float zp; + float wp; + } vector; + }; + + instr_common common; + instr_init init; + instr_deferInit deferInit; + instr_create create; + instr_createQml createQml; + instr_completeQml completeQml; + instr_createSimple createSimple; + instr_storeMeta storeMeta; + instr_setId setId; + instr_assignValueSource assignValueSource; + instr_assignValueInterceptor assignValueInterceptor; + instr_initV8Bindings initV8Bindings; + instr_assignV4Binding assignV4Binding; + instr_assignBinding assignBinding; + instr_fetch fetch; + instr_fetchValue fetchValue; + instr_fetchQmlList fetchQmlList; + instr_begin begin; + instr_storeFloat storeFloat; + instr_storeDouble storeDouble; + instr_storeInteger storeInteger; + instr_storeBool storeBool; + instr_storeString storeString; + instr_storeTrString storeTrString; + instr_storeTrIdString storeTrIdString; + instr_storeByteArray storeByteArray; + instr_storeScriptString storeScriptString; + instr_storeScript storeScript; + instr_storeUrl storeUrl; + instr_storeColor storeColor; + instr_storeDate storeDate; + instr_storeTime storeTime; + instr_storeDateTime storeDateTime; + instr_storePoint storePoint; + instr_storePointF storePointF; + instr_storeSize storeSize; + instr_storeSizeF storeSizeF; + instr_storeRect storeRect; + instr_storeRectF storeRectF; + instr_storeVector3D storeVector3D; + instr_storeVector4D storeVector4D; + instr_storeObject storeObject; + instr_assignCustomType assignCustomType; + instr_storeSignal storeSignal; + instr_assignSignalObject assignSignalObject; + instr_createComponent createComponent; + instr_fetchAttached fetchAttached; + instr_defer defer; + instr_assignObjectList assignObjectList; + + static int size(Type type); +}; + +template<int N> +struct QQmlInstructionMeta { +}; + +#define QML_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct QQmlInstructionMeta<(int)QQmlInstruction::I> { \ + enum { Size = QML_INSTR_SIZE(I, FMT) }; \ + typedef QQmlInstruction::instr_##FMT DataType; \ + static const DataType &data(const QQmlInstruction &instr) { return instr.FMT; } \ + static void setData(QQmlInstruction &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_QML_INSTR(QML_INSTR_META_TEMPLATE); +#undef QML_INSTR_META_TEMPLATE + +template<int Instr> +class QQmlInstructionData : public QQmlInstructionMeta<Instr>::DataType +{ +}; + +QT_END_NAMESPACE + +#endif // QQMLINSTRUCTION_P_H diff --git a/src/qml/qml/qqmlintegercache.cpp b/src/qml/qml/qqmlintegercache.cpp new file mode 100644 index 0000000000..88ea3af2de --- /dev/null +++ b/src/qml/qml/qqmlintegercache.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlintegercache_p.h" + +QT_BEGIN_NAMESPACE + +QQmlIntegerCache::QQmlIntegerCache() +{ +} + +QQmlIntegerCache::~QQmlIntegerCache() +{ +} + +QString QQmlIntegerCache::findId(int value) const +{ + for (StringCache::ConstIterator iter = stringCache.begin(); + iter != stringCache.end(); ++iter) { + if (iter.value() == value) + return iter.key(); + } + return QString(); +} + +void QQmlIntegerCache::reserve(int size) +{ + stringCache.reserve(size); +} + +void QQmlIntegerCache::add(const QString &id, int value) +{ + Q_ASSERT(!stringCache.contains(id)); + + stringCache.insert(id, value); +} + +int QQmlIntegerCache::value(const QString &id) +{ + int *rv = stringCache.value(id); + return rv?*rv:-1; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlintegercache_p.h b/src/qml/qml/qqmlintegercache_p.h new file mode 100644 index 0000000000..317a86b45a --- /dev/null +++ b/src/qml/qml/qqmlintegercache_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINTEGERCACHE_P_H +#define QQMLINTEGERCACHE_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/qhashedstring_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlType; +class QQmlEngine; +class QQmlIntegerCache : public QQmlRefCount +{ +public: + QQmlIntegerCache(); + virtual ~QQmlIntegerCache(); + + inline int count() const; + void add(const QString &, int); + void reserve(int); + + int value(const QString &); + inline int value(const QHashedV8String &); + + QString findId(int value) const; + +private: + typedef QStringHash<int> StringCache; + StringCache stringCache; +}; + +int QQmlIntegerCache::value(const QHashedV8String &name) +{ + int *result = stringCache.value(name); + return result?*result:-1; +} + +int QQmlIntegerCache::count() const +{ + return stringCache.count(); +} + +QT_END_NAMESPACE + +#endif // QQMLINTEGERCACHE_P_H + diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp new file mode 100644 index 0000000000..00fd805ee0 --- /dev/null +++ b/src/qml/qml/qqmllist.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllist.h" +#include "qqmllist_p.h" +#include "qqmlengine_p.h" +#include "qqmlproperty_p.h" + +QT_BEGIN_NAMESPACE + +QQmlListReferencePrivate::QQmlListReferencePrivate() +: propertyType(-1), refCount(1) +{ +} + +QQmlListReference QQmlListReferencePrivate::init(const QQmlListProperty<QObject> &prop, int propType, QQmlEngine *engine) +{ + QQmlListReference rv; + + if (!prop.object) return rv; + + QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):0; + + int listType = p?p->listType(propType):QQmlMetaType::listType(propType); + if (listType == -1) return rv; + + rv.d = new QQmlListReferencePrivate; + rv.d->object = prop.object; + rv.d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject(); + rv.d->property = prop; + rv.d->propertyType = propType; + + return rv; +} + +void QQmlListReferencePrivate::addref() +{ + Q_ASSERT(refCount > 0); + ++refCount; +} + +void QQmlListReferencePrivate::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + if (!refCount) + delete this; +} + +/*! +\class QQmlListReference +\since 4.7 +\module QtQml +\brief The QQmlListReference class allows the manipulation of QQmlListProperty properties. + +QQmlListReference allows C++ programs to read from, and assign values to a QML list property in a +simple and type safe way. A QQmlListReference can be created by passing an object and property +name or through a QQmlProperty instance. These two are equivalant: + +\code +QQmlListReference ref1(object, "children"); + +QQmlProperty ref2(object, "children"); +QQmlListReference ref2 = qvariant_cast<QQmlListReference>(ref2.read()); +\endcode + +Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and +canCount() allow programs to query whether an operation is supported on a given property. + +QML list properties are typesafe. Only QObject's that derive from the correct base class can be assigned to +the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported. +Attempting to add objects of the incorrect type to a list property will fail. + +Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure +that it does not request an out of range element using the count() method before calling at(). +*/ + +/*! +Constructs an invalid instance. +*/ +QQmlListReference::QQmlListReference() +: d(0) +{ +} + +/*! +Constructs a QQmlListReference for \a object's \a property. If \a property is not a list +property, an invalid QQmlListReference is created. If \a object is destroyed after +the reference is constructed, it will automatically become invalid. That is, it is safe to hold +QQmlListReference instances even after \a object is deleted. + +Passing \a engine is required to access some QML created list properties. If in doubt, and an engine +is available, pass it. +*/ +QQmlListReference::QQmlListReference(QObject *object, const char *property, QQmlEngine *engine) +: d(0) +{ + if (!object || !property) return; + + QQmlPropertyData local; + QQmlPropertyData *data = + QQmlPropertyCache::property(engine, object, QLatin1String(property), local); + + if (!data || !data->isQList()) return; + + QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):0; + + int listType = p?p->listType(data->propType):QQmlMetaType::listType(data->propType); + if (listType == -1) return; + + d = new QQmlListReferencePrivate; + d->object = object; + d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject(); + d->propertyType = data->propType; + + void *args[] = { &d->property, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex, args); +} + +/*! \internal */ +QQmlListReference::QQmlListReference(const QQmlListReference &o) +: d(o.d) +{ + if (d) d->addref(); +} + +/*! \internal */ +QQmlListReference &QQmlListReference::operator=(const QQmlListReference &o) +{ + if (o.d) o.d->addref(); + if (d) d->release(); + d = o.d; + return *this; +} + +/*! \internal */ +QQmlListReference::~QQmlListReference() +{ + if (d) d->release(); +} + +/*! +Returns true if the instance refers to a valid list property, otherwise false. +*/ +bool QQmlListReference::isValid() const +{ + return d && d->object; +} + +/*! +Returns the list property's object. Returns 0 if the reference is invalid. +*/ +QObject *QQmlListReference::object() const +{ + if (isValid()) return d->object; + else return 0; +} + +/*! +Returns the QMetaObject for the elements stored in the list property. Returns 0 if the reference +is invalid. + +The QMetaObject can be used ahead of time to determine whether a given instance can be added +to a list. +*/ +const QMetaObject *QQmlListReference::listElementType() const +{ + if (isValid()) return d->elementType; + else return 0; +} + +/*! +Returns true if the list property can be appended to, otherwise false. Returns false if the +reference is invalid. + +\sa append() +*/ +bool QQmlListReference::canAppend() const +{ + return (isValid() && d->property.append); +} + +/*! +Returns true if the list property can queried by index, otherwise false. Returns false if the +reference is invalid. + +\sa at() +*/ +bool QQmlListReference::canAt() const +{ + return (isValid() && d->property.at); +} + +/*! +Returns true if the list property can be cleared, otherwise false. Returns false if the +reference is invalid. + +\sa clear() +*/ +bool QQmlListReference::canClear() const +{ + return (isValid() && d->property.clear); +} + +/*! +Returns true if the list property can be queried for its element count, otherwise false. +Returns false if the reference is invalid. + +\sa count() +*/ +bool QQmlListReference::canCount() const +{ + return (isValid() && d->property.count); +} + +/*! +Appends \a object to the list. Returns true if the operation succeeded, otherwise false. + +\sa canAppend() +*/ +bool QQmlListReference::append(QObject *object) const +{ + if (!canAppend()) return false; + + if (object && !QQmlPropertyPrivate::canConvert(object->metaObject(), d->elementType)) + return false; + + d->property.append(&d->property, object); + + return true; +} + +/*! +Returns the list element at \a index, or 0 if the operation failed. + +\sa canAt() +*/ +QObject *QQmlListReference::at(int index) const +{ + if (!canAt()) return 0; + + return d->property.at(&d->property, index); +} + +/*! +Clears the list. Returns true if the operation succeeded, otherwise false. + +\sa canClear() +*/ +bool QQmlListReference::clear() const +{ + if (!canClear()) return false; + + d->property.clear(&d->property); + + return true; +} + +/*! +Returns the number of objects in the list, or 0 if the operation failed. +*/ +int QQmlListReference::count() const +{ + if (!canCount()) return 0; + + return d->property.count(&d->property); +} + +/*! +\class QQmlListProperty +\since 4.7 +\brief The QQmlListProperty class allows applications to expose list-like +properties to QML. + +QML has many list properties, where more than one object value can be assigned. +The use of a list property from QML looks like this: + +\code +FruitBasket { + fruit: [ + Apple {}, + Orange{}, + Banana{} + ] +} +\endcode + +The QQmlListProperty encapsulates a group of function pointers that represet the +set of actions QML can perform on the list - adding items, retrieving items and +clearing the list. In the future, additional operations may be supported. All +list properties must implement the append operation, but the rest are optional. + +To provide a list property, a C++ class must implement the operation callbacks, +and then return an appropriate QQmlListProperty value from the property getter. +List properties should have no setter. In the example above, the Q_PROPERTY() +declarative will look like this: + +\code +Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit); +\endcode + +QML list properties are typesafe - in this case \c {Fruit} is a QObject type that +\c {Apple}, \c {Orange} and \c {Banana} all derive from. + +\note QQmlListProperty can only be used for lists of QObject-derived object pointers. + +\sa {Object and List Property Types} + +*/ + +/*! +\fn QQmlListProperty::QQmlListProperty() +\internal +*/ + +/*! +\fn QQmlListProperty::QQmlListProperty(QObject *object, QList<T *> &list) + +Convenience constructor for making a QQmlListProperty value from an existing +QList \a list. The \a list reference must remain valid for as long as \a object +exists. \a object must be provided. + +Generally this constructor should not be used in production code, as a +writable QList violates QML's memory management rules. However, this constructor +can very useful while prototyping. +*/ + +/*! +\fn QQmlListProperty::QQmlListProperty(QObject *object, void *data, AppendFunction append, + CountFunction count = 0, AtFunction at = 0, + ClearFunction clear = 0) + +Construct a QQmlListProperty from a set of operation functions. An opaque \a data handle +may be passed which can be accessed from within the operation functions. The list property +remains valid while \a object exists. + +The \a append operation is compulsory and must be provided, while the \a count, \a at and +\a clear methods are optional. +*/ + +/*! +\typedef QQmlListProperty::AppendFunction + +Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}. + +Append the \a value to the list \a property. +*/ + +/*! +\typedef QQmlListProperty::CountFunction + +Synonym for \c {int (*)(QQmlListProperty<T> *property)}. + +Return the number of elements in the list \a property. +*/ + +/*! +\fn bool QQmlListProperty::operator==(const QQmlListProperty &other) const + +Returns true if this QQmlListProperty is equal to \a other, otherwise false. +*/ + +/*! +\typedef QQmlListProperty::AtFunction + +Synonym for \c {T *(*)(QQmlListProperty<T> *property, int index)}. + +Return the element at position \a index in the list \a property. +*/ + +/*! +\typedef QQmlListProperty::ClearFunction + +Synonym for \c {void (*)(QQmlListProperty<T> *property)}. + +Clear the list \a property. +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h new file mode 100644 index 0000000000..3f23cedbc2 --- /dev/null +++ b/src/qml/qml/qqmllist.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLIST_H +#define QQMLLIST_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QObject; +struct QMetaObject; + +#ifndef QQMLLISTPROPERTY +#define QQMLLISTPROPERTY +template<typename T> +class QQmlListProperty { +public: + typedef void (*AppendFunction)(QQmlListProperty<T> *, T*); + typedef int (*CountFunction)(QQmlListProperty<T> *); + typedef T *(*AtFunction)(QQmlListProperty<T> *, int); + typedef void (*ClearFunction)(QQmlListProperty<T> *); + + QQmlListProperty() + : object(0), data(0), append(0), count(0), at(0), clear(0), dummy1(0), dummy2(0) {} + QQmlListProperty(QObject *o, QList<T *> &list) + : object(o), data(&list), append(qlist_append), count(qlist_count), at(qlist_at), + clear(qlist_clear), dummy1(0), dummy2(0) {} + QQmlListProperty(QObject *o, void *d, AppendFunction a, CountFunction c = 0, AtFunction t = 0, + ClearFunction r = 0) + : object(o), data(d), append(a), count(c), at(t), clear(r), dummy1(0), dummy2(0) {} + + bool operator==(const QQmlListProperty &o) const { + return object == o.object && + data == o.data && + append == o.append && + count == o.count && + at == o.at && + clear == o.clear; + } + + QObject *object; + void *data; + + AppendFunction append; + + CountFunction count; + AtFunction at; + + ClearFunction clear; + + void *dummy1; + void *dummy2; + +private: + static void qlist_append(QQmlListProperty *p, T *v) { + reinterpret_cast<QList<T *> *>(p->data)->append(v); + } + static int qlist_count(QQmlListProperty *p) { + return reinterpret_cast<QList<T *> *>(p->data)->count(); + } + static T *qlist_at(QQmlListProperty *p, int idx) { + return reinterpret_cast<QList<T *> *>(p->data)->at(idx); + } + static void qlist_clear(QQmlListProperty *p) { + return reinterpret_cast<QList<T *> *>(p->data)->clear(); + } +}; +#endif + +class QQmlEngine; +class QQmlListReferencePrivate; +class Q_QML_EXPORT QQmlListReference +{ +public: + QQmlListReference(); + QQmlListReference(QObject *, const char *property, QQmlEngine * = 0); + QQmlListReference(const QQmlListReference &); + QQmlListReference &operator=(const QQmlListReference &); + ~QQmlListReference(); + + bool isValid() const; + + QObject *object() const; + const QMetaObject *listElementType() const; + + bool canAppend() const; + bool canAt() const; + bool canClear() const; + bool canCount() const; + + bool append(QObject *) const; + QObject *at(int) const; + bool clear() const; + int count() const; + +private: + friend class QQmlListReferencePrivate; + QQmlListReferencePrivate* d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlListReference) + +QT_END_HEADER + +#endif // QQMLLIST_H diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h new file mode 100644 index 0000000000..0fe0ed3d44 --- /dev/null +++ b/src/qml/qml/qqmllist_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLIST_P_H +#define QQMLLIST_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 "qqmllist.h" +#include "qqmlguard_p.h" + +QT_BEGIN_NAMESPACE + +class QQmlListReferencePrivate +{ +public: + QQmlListReferencePrivate(); + + static QQmlListReference init(const QQmlListProperty<QObject> &, int, QQmlEngine *); + + QQmlGuard<QObject> object; + const QMetaObject *elementType; + QQmlListProperty<QObject> property; + int propertyType; + + void addref(); + void release(); + int refCount; + + static inline QQmlListReferencePrivate *get(QQmlListReference *ref) { + return ref->d; + } +}; + + +QT_END_NAMESPACE + +#endif // QQMLLIST_P_H diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp new file mode 100644 index 0000000000..4dc30f27ea --- /dev/null +++ b/src/qml/qml/qqmllocale.cpp @@ -0,0 +1,1123 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllocale_p.h" +#include "qqmlengine_p.h" +#include <private/qqmlcontext_p.h> +#include <private/qjsconverter_impl_p.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qdatetime.h> + +#include <private/qlocale_p.h> +#include <private/qlocale_data_p.h> + +QT_BEGIN_NAMESPACE + +class QV8LocaleDataResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(LocaleDataType) +public: + QV8LocaleDataResource(QV8Engine *e) : QV8ObjectResource(e) {} + QLocale locale; +}; + +#define GET_LOCALE_DATA_RESOURCE(OBJECT) \ +QV8LocaleDataResource *r = v8_resource_cast<QV8LocaleDataResource>(OBJECT); \ +if (!r) \ + V8THROW_ERROR("Not a valid Locale object") + +static bool isLocaleObject(v8::Handle<v8::Value> val) +{ + if (!val->IsObject()) + return false; + + v8::Handle<v8::Object> localeObj = val->ToObject(); + return localeObj->Has(v8::String::New("nativeLanguageName")); //XXX detect locale object properly +} + +//-------------- +// Date extension + +static const char *dateToLocaleStringFunction = + "(function(toLocaleStringFunc) { " + " var orig_toLocaleString;" + " orig_toLocaleString = Date.prototype.toLocaleString;" + " Date.prototype.toLocaleString = (function() {" + " var val = toLocaleStringFunc.apply(this, arguments);" + " if (val == undefined) val = orig_toLocaleString.call(this);" + " return val;" + " })" + "})"; + +static const char *dateToLocaleTimeStringFunction = + "(function(toLocaleTimeStringFunc) { " + " var orig_toLocaleTimeString;" + " orig_toLocaleTimeString = Date.prototype.toLocaleTimeString;" + " Date.prototype.toLocaleTimeString = (function() {" + " var val = toLocaleTimeStringFunc.apply(this, arguments);" + " if (val == undefined) val = orig_toLocaleTimeString.call(this);" + " return val;" + " })" + "})"; + +static const char *dateToLocaleDateStringFunction = + "(function(toLocaleDateStringFunc) { " + " var orig_toLocaleDateString;" + " orig_toLocaleDateString = Date.prototype.toLocaleDateString;" + " Date.prototype.toLocaleDateString = (function() {" + " var val = toLocaleDateStringFunc.apply(this, arguments);" + " if (val == undefined) val = orig_toLocaleDateString.call(this);" + " return val;" + " })" + "})"; + + +static const char *dateFromLocaleStringFunction = + "(function(fromLocaleStringFunc) { " + " Date.fromLocaleString = (function() {" + " return fromLocaleStringFunc.apply(null, arguments);" + " })" + "})"; + +static const char *dateFromLocaleTimeStringFunction = + "(function(fromLocaleTimeStringFunc) { " + " Date.fromLocaleTimeString = (function() {" + " return fromLocaleTimeStringFunc.apply(null, arguments);" + " })" + "})"; + +static const char *dateFromLocaleDateStringFunction = + "(function(fromLocaleDateStringFunc) { " + " Date.fromLocaleDateString = (function() {" + " return fromLocaleDateStringFunc.apply(null, arguments);" + " })" + "})"; + + +static void registerFunction(QV8Engine *engine, const char *script, v8::InvocationCallback func) +{ + v8::Local<v8::Script> registerScript = v8::Script::New(v8::String::New(script), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode); + v8::Local<v8::Value> result = registerScript->Run(); + Q_ASSERT(result->IsFunction()); + v8::Local<v8::Function> registerFunc = v8::Local<v8::Function>::Cast(result); + v8::Handle<v8::Value> args = V8FUNCTION(func, engine); + registerFunc->Call(v8::Local<v8::Object>::Cast(registerFunc), 1, &args); +} + +void QQmlDateExtension::registerExtension(QV8Engine *engine) +{ + registerFunction(engine, dateToLocaleStringFunction, toLocaleString); + registerFunction(engine, dateToLocaleTimeStringFunction, toLocaleTimeString); + registerFunction(engine, dateToLocaleDateStringFunction, toLocaleDateString); + registerFunction(engine, dateFromLocaleStringFunction, fromLocaleString); + registerFunction(engine, dateFromLocaleTimeStringFunction, fromLocaleTimeString); + registerFunction(engine, dateFromLocaleDateStringFunction, fromLocaleDateString); +} + +v8::Handle<v8::Value> QQmlDateExtension::toLocaleString(const v8::Arguments& args) +{ + if (args.Length() > 2) + return v8::Undefined(); + + if (!args.This()->IsDate()) + return v8::Undefined(); + + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(args.This())->NumberValue()); + + if (args.Length() == 0) { + // Use QLocale for standard toLocaleString() function + QLocale locale; + return QJSConverter::toString(locale.toString(dt)); + } + + if (!isLocaleObject(args[0])) + return v8::Undefined(); // Use the default Date toLocaleString() + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QString formattedDt; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = r->engine->toVariant(args[1], -1).toString(); + formattedDt = r->locale.toString(dt, format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + formattedDt = r->locale.toString(dt, format); + } else { + V8THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format"); + } + } else { + formattedDt = r->locale.toString(dt, enumFormat); + } + + return r->engine->toString(formattedDt); +} + +v8::Handle<v8::Value> QQmlDateExtension::toLocaleTimeString(const v8::Arguments& args) +{ + if (args.Length() > 2) + return v8::Undefined(); + + if (!args.This()->IsDate()) + return v8::Undefined(); + + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(args.This())->NumberValue()); + QTime time = dt.time(); + + if (args.Length() == 0) { + // Use QLocale for standard toLocaleString() function + QLocale locale; + return QJSConverter::toString(locale.toString(time)); + } + + if (!isLocaleObject(args[0])) + return v8::Undefined(); // Use the default Date toLocaleTimeString() + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QString formattedTime; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = r->engine->toVariant(args[1], -1).toString(); + formattedTime = r->locale.toString(time, format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + formattedTime = r->locale.toString(time, format); + } else { + V8THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format"); + } + } else { + formattedTime = r->locale.toString(time, enumFormat); + } + + return r->engine->toString(formattedTime); +} + +v8::Handle<v8::Value> QQmlDateExtension::toLocaleDateString(const v8::Arguments& args) +{ + if (args.Length() > 2) + return v8::Undefined(); + + if (!args.This()->IsDate()) + return v8::Undefined(); + + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(args.This())->NumberValue()); + QDate date = dt.date(); + + if (args.Length() == 0) { + // Use QLocale for standard toLocaleString() function + QLocale locale; + return QJSConverter::toString(locale.toString(date)); + } + + if (!isLocaleObject(args[0])) + return v8::Undefined(); // Use the default Date toLocaleDateString() + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QString formattedDate; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = r->engine->toVariant(args[1], -1).toString(); + formattedDate = r->locale.toString(date, format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + formattedDate = r->locale.toString(date, format); + } else { + V8THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format"); + } + } else { + formattedDate = r->locale.toString(date, enumFormat); + } + + return r->engine->toString(formattedDate); +} + +v8::Handle<v8::Value> QQmlDateExtension::fromLocaleString(const v8::Arguments& args) +{ + if (args.Length() == 1 && args[0]->IsString()) { + QLocale locale; + QString dateString = QJSConverter::toString(args[0]->ToString()); + QDateTime dt = locale.toDateTime(dateString); + return QJSConverter::toDateTime(dt); + } + + if (args.Length() < 1 || args.Length() > 3 || !isLocaleObject(args[0])) + V8THROW_ERROR("Locale: Date.fromLocaleString(): Invalid arguments"); + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QDateTime dt; + QString dateString = r->engine->toString(args[1]->ToString()); + if (args.Length() == 3) { + if (args[2]->IsString()) { + QString format = r->engine->toString(args[2]->ToString()); + dt = r->locale.toDateTime(dateString, format); + } else if (args[2]->IsNumber()) { + quint32 intFormat = args[2]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + dt = r->locale.toDateTime(dateString, format); + } else { + V8THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format"); + } + } else { + dt = r->locale.toDateTime(dateString, enumFormat); + } + + return QJSConverter::toDateTime(dt); +} + +v8::Handle<v8::Value> QQmlDateExtension::fromLocaleTimeString(const v8::Arguments& args) +{ + if (args.Length() == 1 && args[0]->IsString()) { + QLocale locale; + QString timeString = QJSConverter::toString(args[0]->ToString()); + QTime time = locale.toTime(timeString); + QDateTime dt = QDateTime::currentDateTime(); + dt.setTime(time); + return QJSConverter::toDateTime(dt); + } + + if (args.Length() < 1 || args.Length() > 3 || !isLocaleObject(args[0])) + V8THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid arguments"); + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QTime tm; + QString dateString = r->engine->toString(args[1]->ToString()); + if (args.Length() == 3) { + if (args[2]->IsString()) { + QString format = r->engine->toString(args[2]->ToString()); + tm = r->locale.toTime(dateString, format); + } else if (args[2]->IsNumber()) { + quint32 intFormat = args[2]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + tm = r->locale.toTime(dateString, format); + } else { + V8THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format"); + } + } else { + tm = r->locale.toTime(dateString, enumFormat); + } + + QDateTime dt = QDateTime::currentDateTime(); + dt.setTime(tm); + + return QJSConverter::toDateTime(dt); +} + +v8::Handle<v8::Value> QQmlDateExtension::fromLocaleDateString(const v8::Arguments& args) +{ + if (args.Length() == 1 && args[0]->IsString()) { + QLocale locale; + QString dateString = QJSConverter::toString(args[0]->ToString()); + QDate date = locale.toDate(dateString); + return QJSConverter::toDateTime(QDateTime(date)); + } + + if (args.Length() < 1 || args.Length() > 3 || !isLocaleObject(args[0])) + V8THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid arguments"); + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QLocale::FormatType enumFormat = QLocale::LongFormat; + QDate dt; + QString dateString = r->engine->toString(args[1]->ToString()); + if (args.Length() == 3) { + if (args[2]->IsString()) { + QString format = r->engine->toString(args[2]->ToString()); + dt = r->locale.toDate(dateString, format); + } else if (args[2]->IsNumber()) { + quint32 intFormat = args[2]->ToNumber()->Value(); + QLocale::FormatType format = QLocale::FormatType(intFormat); + dt = r->locale.toDate(dateString, format); + } else { + V8THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format"); + } + } else { + dt = r->locale.toDate(dateString, enumFormat); + } + + return QJSConverter::toDateTime(QDateTime(dt)); +} + +//----------------- +// Number extension + +static const char *numberToLocaleStringFunction = + "(function(toLocaleStringFunc) { " + " var orig_toLocaleString;" + " orig_toLocaleString = Number.prototype.toLocaleString;" + " Number.prototype.toLocaleString = (function() {" + " var val = toLocaleStringFunc.apply(this, arguments);" + " if (val == undefined) val = orig_toLocaleString.call(this);" + " return val;" + " })" + "})"; + +static const char *numberToLocaleCurrencyStringFunction = + "(function(toLocaleCurrencyStringFunc) { " + " Number.prototype.toLocaleCurrencyString = (function() {" + " return toLocaleCurrencyStringFunc.apply(this, arguments);" + " })" + "})"; + +static const char *numberFromLocaleStringFunction = + "(function(fromLocaleStringFunc) { " + " Number.fromLocaleString = (function() {" + " return fromLocaleStringFunc.apply(null, arguments);" + " })" + "})"; + + +void QQmlNumberExtension::registerExtension(QV8Engine *engine) +{ + registerFunction(engine, numberToLocaleStringFunction, toLocaleString); + registerFunction(engine, numberToLocaleCurrencyStringFunction, toLocaleCurrencyString); + registerFunction(engine, numberFromLocaleStringFunction, fromLocaleString); +} + +v8::Handle<v8::Value> QQmlNumberExtension::toLocaleString(const v8::Arguments& args) +{ + if (args.Length() > 3) + V8THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + + double number = args.This()->ToNumber()->Value(); + + if (args.Length() == 0) { + // Use QLocale for standard toLocaleString() function + QLocale locale; + return QJSConverter::toString(locale.toString(number)); + } + + if (!isLocaleObject(args[0])) + return v8::Undefined(); // Use the default Number toLocaleString() + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + uint16_t format = 'f'; + if (args.Length() > 1) { + if (!args[1]->IsString()) + V8THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + v8::Local<v8::String> fs = args[1]->ToString(); + if (!fs.IsEmpty() && fs->Length()) + format = fs->GetCharacter(0); + } + int prec = 2; + if (args.Length() > 2) { + if (!args[2]->IsNumber()) + V8THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + prec = args[2]->IntegerValue(); + } + + return r->engine->toString(r->locale.toString(number, (char)format, prec)); +} + +v8::Handle<v8::Value> QQmlNumberExtension::toLocaleCurrencyString(const v8::Arguments& args) +{ + if (args.Length() > 2) + V8THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); + + double number = args.This()->ToNumber()->Value(); + + if (args.Length() == 0) { + // Use QLocale for standard toLocaleString() function + QLocale locale; + return QJSConverter::toString(locale.toString(number)); + } + + if (!isLocaleObject(args[0])) + V8THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments"); + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + + QString symbol; + if (args.Length() > 1) { + if (!args[1]->IsString()) + V8THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); + symbol = r->engine->toString(args[1]->ToString()); + } + + return r->engine->toString(r->locale.toCurrencyString(number, symbol)); +} + +v8::Handle<v8::Value> QQmlNumberExtension::fromLocaleString(const v8::Arguments& args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); + + int numberIdx = 0; + QLocale locale; + + if (args.Length() == 2) { + if (!isLocaleObject(args[0])) + V8THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments"); + + GET_LOCALE_DATA_RESOURCE(args[0]->ToObject()); + locale = r->locale; + + numberIdx = 1; + } + + v8::Local<v8::String> ns = args[numberIdx]->ToString(); + if (ns.IsEmpty() || ns->Length() == 0) + return v8::Number::New(Q_QNAN); + + bool ok = false; + double val = locale.toDouble(QJSConverter::toString(ns), &ok); + + if (!ok) + V8THROW_ERROR("Locale: Number.fromLocaleString(): Invalid format") + + return v8::Number::New(val); +} + +//-------------- +// Locale object + +static v8::Handle<v8::Value> locale_get_firstDayOfWeek(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + GET_LOCALE_DATA_RESOURCE(info.This()); + return v8::Integer::New(r->locale.firstDayOfWeek()); +} + +static v8::Handle<v8::Value> locale_get_measurementSystem(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + GET_LOCALE_DATA_RESOURCE(info.This()); + return v8::Integer::New(r->locale.measurementSystem()); +} + +static v8::Handle<v8::Value> locale_get_textDirection(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + GET_LOCALE_DATA_RESOURCE(info.This()); + return v8::Integer::New(r->locale.textDirection()); +} + +static v8::Handle<v8::Value> locale_get_weekDays(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + GET_LOCALE_DATA_RESOURCE(info.This()); + + QList<Qt::DayOfWeek> days = r->locale.weekdays(); + + v8::Handle<v8::Array> result = v8::Array::New(days.size()); + for (int i = 0; i < days.size(); ++i) { + int day = days.at(i); + if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday) + day = 0; + result->Set(i, v8::Integer::New(day)); + } + + return result; +} + +static v8::Handle<v8::Value> locale_get_uiLanguages(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + GET_LOCALE_DATA_RESOURCE(info.This()); + + QStringList langs = r->locale.uiLanguages(); + v8::Handle<v8::Array> result = v8::Array::New(langs.size()); + for (int i = 0; i < langs.size(); ++i) { + result->Set(i, r->engine->toString(langs.at(i))); + } + + return result; +} + +static v8::Handle<v8::Value> locale_currencySymbol(const v8::Arguments &args) +{ + GET_LOCALE_DATA_RESOURCE(args.This()); + + if (args.Length() > 1) + V8THROW_ERROR("Locale: currencySymbol(): Invalid arguments"); + + QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol; + if (args.Length() == 1) { + quint32 intFormat = args[0]->ToNumber()->Value(); + format = QLocale::CurrencySymbolFormat(intFormat); + } + + return r->engine->toString(r->locale.currencySymbol(format)); +} + +#define LOCALE_FORMAT(FUNC) \ +static v8::Handle<v8::Value> locale_ ##FUNC (const v8::Arguments &args) { \ + GET_LOCALE_DATA_RESOURCE(args.This());\ + if (args.Length() > 1) \ + V8THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \ + QLocale::FormatType format = QLocale::LongFormat;\ + if (args.Length() == 1) { \ + quint32 intFormat = args[0]->Uint32Value(); \ + format = QLocale::FormatType(intFormat); \ + } \ + return r->engine->toString(r->locale. FUNC (format)); \ +} + +LOCALE_FORMAT(dateTimeFormat) +LOCALE_FORMAT(timeFormat) +LOCALE_FORMAT(dateFormat) + +// +1 added to idx because JS is 0-based, whereas QLocale months begin at 1. +#define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \ +static v8::Handle<v8::Value> locale_ ## VARIABLE (const v8::Arguments &args) {\ + GET_LOCALE_DATA_RESOURCE(args.This()); \ + if (args.Length() < 1 || args.Length() > 2) \ + V8THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ + QLocale::FormatType enumFormat = QLocale::LongFormat; \ + int idx = args[0]->IntegerValue() + 1; \ + if (idx < 1 || idx > 12) \ + V8THROW_ERROR("Locale: Invalid month"); \ + QString name; \ + if (args.Length() == 2) { \ + if (args[1]->IsNumber()) { \ + quint32 intFormat = args[1]->IntegerValue(); \ + QLocale::FormatType format = QLocale::FormatType(intFormat); \ + name = r->locale. VARIABLE(idx, format); \ + } else { \ + V8THROW_ERROR("Locale: Invalid datetime format"); \ + } \ + } else { \ + name = r->locale. VARIABLE(idx, enumFormat); \ + } \ + return r->engine->toString(name); \ +} + +// 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date +#define LOCALE_FORMATTED_DAYNAME(VARIABLE) \ +static v8::Handle<v8::Value> locale_ ## VARIABLE (const v8::Arguments &args) {\ + GET_LOCALE_DATA_RESOURCE(args.This()); \ + if (args.Length() < 1 || args.Length() > 2) \ + V8THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \ + QLocale::FormatType enumFormat = QLocale::LongFormat; \ + int idx = args[0]->IntegerValue(); \ + if (idx < 0 || idx > 7) \ + V8THROW_ERROR("Locale: Invalid day"); \ + if (idx == 0) idx = 7; \ + QString name; \ + if (args.Length() == 2) { \ + if (args[1]->IsNumber()) { \ + quint32 intFormat = args[1]->ToNumber()->Value(); \ + QLocale::FormatType format = QLocale::FormatType(intFormat); \ + name = r->locale. VARIABLE(idx, format); \ + } else { \ + V8THROW_ERROR("Locale: Invalid datetime format"); \ + } \ + } else { \ + name = r->locale. VARIABLE(idx, enumFormat); \ + } \ + return r->engine->toString(name); \ +} + + +#define LOCALE_REGISTER_FORMATTED_NAME_FUNCTION(FT, VARIABLE, ENGINE) \ + FT->PrototypeTemplate()->Set(v8::String::New( #VARIABLE ), V8FUNCTION(locale_ ## VARIABLE, ENGINE)); + +LOCALE_FORMATTED_MONTHNAME(monthName) +LOCALE_FORMATTED_MONTHNAME(standaloneMonthName) +LOCALE_FORMATTED_DAYNAME(dayName) +LOCALE_FORMATTED_DAYNAME(standaloneDayName) + +#define LOCALE_STRING_PROPERTY(VARIABLE) static v8::Handle<v8::Value> locale_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \ +{ \ + GET_LOCALE_DATA_RESOURCE(info.This()); \ + return r->engine->toString(r->locale. VARIABLE());\ +} + +#define LOCALE_REGISTER_STRING_ACCESSOR(FT, VARIABLE) \ + FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #VARIABLE ), locale_get_ ## VARIABLE ) + + +LOCALE_STRING_PROPERTY(name) +LOCALE_STRING_PROPERTY(nativeLanguageName) +LOCALE_STRING_PROPERTY(nativeCountryName) +LOCALE_STRING_PROPERTY(decimalPoint) +LOCALE_STRING_PROPERTY(groupSeparator) +LOCALE_STRING_PROPERTY(percent) +LOCALE_STRING_PROPERTY(zeroDigit) +LOCALE_STRING_PROPERTY(negativeSign) +LOCALE_STRING_PROPERTY(positiveSign) +LOCALE_STRING_PROPERTY(exponential) +LOCALE_STRING_PROPERTY(amText) +LOCALE_STRING_PROPERTY(pmText) + +class QV8LocaleDataDeletable : public QV8Engine::Deletable +{ +public: + QV8LocaleDataDeletable(QV8Engine *engine); + ~QV8LocaleDataDeletable(); + + v8::Persistent<v8::Function> constructor; +}; + +QV8LocaleDataDeletable::QV8LocaleDataDeletable(QV8Engine *engine) +{ + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + + LOCALE_REGISTER_STRING_ACCESSOR(ft, name); + LOCALE_REGISTER_STRING_ACCESSOR(ft, nativeLanguageName); + LOCALE_REGISTER_STRING_ACCESSOR(ft, nativeCountryName); + LOCALE_REGISTER_STRING_ACCESSOR(ft, decimalPoint); + LOCALE_REGISTER_STRING_ACCESSOR(ft, groupSeparator); + LOCALE_REGISTER_STRING_ACCESSOR(ft, percent); + LOCALE_REGISTER_STRING_ACCESSOR(ft, zeroDigit); + LOCALE_REGISTER_STRING_ACCESSOR(ft, negativeSign); + LOCALE_REGISTER_STRING_ACCESSOR(ft, positiveSign); + LOCALE_REGISTER_STRING_ACCESSOR(ft, exponential); + LOCALE_REGISTER_STRING_ACCESSOR(ft, amText); + LOCALE_REGISTER_STRING_ACCESSOR(ft, pmText); + + ft->PrototypeTemplate()->Set(v8::String::New("currencySymbol"), V8FUNCTION(locale_currencySymbol, engine)); + + ft->PrototypeTemplate()->Set(v8::String::New("dateTimeFormat"), V8FUNCTION(locale_dateTimeFormat, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("dateFormat"), V8FUNCTION(locale_dateFormat, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("timeFormat"), V8FUNCTION(locale_timeFormat, engine)); + + LOCALE_REGISTER_FORMATTED_NAME_FUNCTION(ft, monthName, engine); + LOCALE_REGISTER_FORMATTED_NAME_FUNCTION(ft, standaloneMonthName, engine); + LOCALE_REGISTER_FORMATTED_NAME_FUNCTION(ft, dayName, engine); + LOCALE_REGISTER_FORMATTED_NAME_FUNCTION(ft, standaloneDayName, engine); + + ft->PrototypeTemplate()->SetAccessor(v8::String::New("firstDayOfWeek"), locale_get_firstDayOfWeek); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("weekDays"), locale_get_weekDays); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("measurementSystem"), locale_get_measurementSystem); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("textDirection"), locale_get_textDirection); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("uiLanguages"), locale_get_uiLanguages); + + constructor = qPersistentNew(ft->GetFunction()); +} + +QV8LocaleDataDeletable::~QV8LocaleDataDeletable() +{ + qPersistentDispose(constructor); +} + +V8_DEFINE_EXTENSION(QV8LocaleDataDeletable, localeV8Data); + +/*! + \qmlclass Locale QQmlLocale + \inqmlmodule QtQuick 2 + \brief The Locale object provides locale specific properties and formatted data. + + The Locale object is created via the \l{QML:Qt::locale()}{Qt.locale()} function. It cannot be created + directly. + + The \l{QML:Qt::locale()}{Qt.locale()} function returns a JS Locale object representing the + locale with the specified name, which has the format + "language[_territory][.codeset][@modifier]" or "C". + + Locale supports the concept of a default locale, which is + determined from the system's locale settings at application + startup. If no parameter is passed to Qt.locale() the default + locale object is returned. + + The Locale object provides a number of functions and properties + providing data for the specified locale. + + The Locale object may also be passed to the \l Date and \l Number toLocaleString() + and fromLocaleString() methods in order to convert to/from strings using + the specified locale. + + This example shows the current date formatted for the German locale: + + \code + import QtQuick 2.0 + + Text { + text: "The date is: " + Date().toLocaleString(Qt.locale("de_DE")) + } + \endcode + + The following example displays the specified number + in the correct format for the default locale: + + \code + import QtQuick 2.0 + + Text { + text: "The value is: " + Number(23443.34).toLocaleString(Qt.locale()) + } + \endcode + + QtQuick Locale's data is based on Common Locale Data Repository v1.8.1. + + The double-to-string and string-to-double conversion functions are + covered by the following licenses: + + \legalese + Copyright (c) 1991 by AT&T. + + Permission to use, copy, modify, and distribute this software for any + purpose without fee is hereby granted, provided that this entire notice + is included in all copies of any software which is or includes a copy + or modification of this software and in all copies of the supporting + documentation for such software. + + THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY + REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + + This product includes software developed by the University of + California, Berkeley and its contributors. + + \sa {QtQuick2::Date}{Date} {QtQuick2::Number}{Number} +*/ + +QQmlLocale::QQmlLocale() +{ +} + +QQmlLocale::~QQmlLocale() +{ +} + +v8::Handle<v8::Value> QQmlLocale::locale(QV8Engine *v8engine, const QString &locale) +{ + QV8LocaleDataDeletable *d = localeV8Data(v8engine); + v8::Local<v8::Object> v8Value = d->constructor->NewInstance(); + QV8LocaleDataResource *r = new QV8LocaleDataResource(v8engine); + if (locale.isEmpty()) + r->locale = QLocale(); + else + r->locale = QLocale(locale); + v8Value->SetExternalResource(r); + + return v8Value; +} + +static const char *localeCompareFunction = + "(function(localeCompareFunc) { " + " var orig_localeCompare;" + " orig_localeCompare = String.prototype.localeCompare;" + " String.prototype.localeCompare = (function() {" + " var val = localeCompareFunc.apply(this, arguments);" + " if (val == undefined) val = orig_localeCompare.call(this);" + " return val;" + " })" + "})"; + +void QQmlLocale::registerStringLocaleCompare(QV8Engine *engine) +{ + registerFunction(engine, localeCompareFunction, localeCompare); +} + +v8::Handle<v8::Value> QQmlLocale::localeCompare(const v8::Arguments &args) +{ + if (args.Length() != 1 || (!args[0]->IsString() && !args[0]->IsStringObject())) + return v8::Undefined(); + + if (!args.This()->IsString() && !args.This()->IsStringObject()) + return v8::Undefined(); + + QString thisString = QJSConverter::toString(args.This()->ToString()); + QString thatString = QJSConverter::toString(args[0]->ToString()); + + return v8::Integer::New(QString::localeAwareCompare(thisString, thatString)); +} + +/*! + \enum QtQuick2::Locale::FormatType + + This enumeration describes the types of format that can be used when + converting Date objects to strings. + + \value LongFormat The long version of day and month names; for + example, returning "January" as a month name. + + \value ShortFormat The short version of day and month names; for + example, returning "Jan" as a month name. + + \value NarrowFormat A special version of day and month names for + use when space is limited; for example, returning "J" as a month + name. Note that the narrow format might contain the same text for + different months and days or it can even be an empty string if the + locale doesn't support narrow names, so you should avoid using it + for date formatting. Also, for the system locale this format is + the same as ShortFormat. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::name + + Holds the language and country of this locale as a + string of the form "language_country", where + language is a lowercase, two-letter ISO 639 language code, + and country is an uppercase, two- or three-letter ISO 3166 country code. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::decimalPoint + + Holds the decimal point character of this locale. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::groupSeparator + + Holds the group separator character of this locale. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::percent + + Holds the percent character of this locale. +*/ + + +/*! + \qmlproperty string QtQuick2::Locale::zeroDigit + + Holds Returns the zero digit character of this locale. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::negativeSign + + Holds the negative sign character of this locale. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::positiveSign + + Holds the positive sign character of this locale. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::exponential + + Holds the exponential character of this locale. +*/ + +/*! + \qmlmethod string QtQuick2::Locale::dateTimeFormat(type) + + Returns the date time format used for the current locale. + \a type specifies the FormatType to return. + + \sa {QtQuick2::Date}{Date} +*/ + +/*! + \qmlmethod string QtQuick2::Locale::dateFormat(type) + + Returns the date format used for the current locale. + \a type specifies the FormatType to return. + + \sa {QtQuick2::Date}{Date} +*/ + +/*! + \qmlmethod string QtQuick2::Locale::timeFormat(type) + + Returns the time format used for the current locale. + \a type specifies the FormatType to return. + + \sa {QtQuick2::Date}{Date} +*/ + +/*! + \qmlmethod string QtQuick2::Locale::monthName(month, type) + + Returns the localized name of \a month (0-11), in the optional + \l FortmatType specified by \a type. + + \note the QLocale C++ API expects a range of (1-12), however Locale.monthName() + expects 0-11 as per the JS Date object. + + \sa dayName(), standaloneMonthName() +*/ + +/*! + \qmlmethod string QtQuick2::Locale::standaloneMonthName(month, type) + + Returns the localized name of \a month (0-11) that is used as a + standalone text, in the optional \l FormatType specified by \a type. + + If the locale information doesn't specify the standalone month + name then return value is the same as in monthName(). + + \note the QLocale C++ API expects a range of (1-12), however Locale.standaloneMonthName() + expects 0-11 as per the JS Date object. + + \sa monthName(), standaloneDayName() +*/ + +/*! + \qmlmethod string QtQuick2::Locale::dayName(day, type) + + Returns the localized name of the \a day (where 0 represents + Sunday, 1 represents Monday and so on), in the optional + \l FormatType specified by \a type. + + \sa monthName(), standaloneDayName() +*/ + +/*! + \qmlmethod string QtQuick2::Locale::standaloneDayName(day, type) + + Returns the localized name of the \a day (where 0 represents + Sunday, 1 represents Monday and so on) that is used as a + standalone text, in the \l FormatType specified by \a type. + + If the locale information does not specify the standalone day + name then return value is the same as in dayName(). + + \sa dayName(), standaloneMonthName() +*/ + +/*! + \qmlproperty enumeration QtQuick2::Locale::firstDayOfWeek + + Holds the first day of the week according to the current locale. + + \list + \o Locale.Sunday = 0 + \o Locale.Monday = 1 + \o Locale.Tuesday = 2 + \o Locale.Wednesday = 3 + \o Locale.Thursday = 4 + \o Locale.Friday = 5 + \o Locale.Saturday = 6 + \endlist + + \note that these values match the JS Date API which is different + from the Qt C++ API where Qt::Sunday = 7. +*/ + +/*! + \qmlproperty Array<int> QtQuick2::Locale::weekDays + + Holds an array of days that are considered week days according to the current locale, + where Sunday is 0 and Saturday is 6. + + \sa firstDayOfWeek +*/ + +/*! + \qmlproperty Array<string> QtQuick2::Locale::uiLanguages + + Returns an ordered list of locale names for translation purposes in + preference order. + + The return value represents locale names that the user expects to see the + UI translation in. + + The first item in the list is the most preferred one. +*/ + +/*! + \qmlproperty enumeration QtQuick2::Locale::textDirection + + Holds the text direction of the language: + \list + \o Qt.LeftToRight + \o Qt.RightToLeft + \endlist +*/ + +/*! + \qmlproperty string QtQuick2::Locale::amText + + The localized name of the "AM" suffix for times specified using the conventions of the 12-hour clock. +*/ + +/*! + \qmlproperty string QtQuick2::Locale::pmText + + The localized name of the "PM" suffix for times specified using the conventions of the 12-hour clock. +*/ + +/*! + \qmlmethod string QtQuick2::Locale::currencySymbol(format) + + Returns the currency symbol for the specified \a format: + \list + \o Locale.CurrencyIsoCode a ISO-4217 code of the currency. + \o Locale.CurrencySymbol a currency symbol. + \o Locale.CurrencyDisplayName a user readable name of the currency. + \endlist + \sa Number::toLocaleCurrencyString() +*/ + +/*! + \qmlproperty string QtQuick2::Locale::nativeLanguageName + + Holds a native name of the language for the locale. For example + "Schwiizertüütsch" for Swiss-German locale. + + \sa nativeCountryName +*/ + +/*! + \qmlproperty string QtQuick2::Locale::nativeCountryName + + Holds a native name of the country for the locale. For example + "España" for Spanish/Spain locale. + + \sa nativeLanguageName +*/ + +/*! + \qmlproperty enumeration QtQuick2::Locale::measurementSystem + + This property defines which units are used for measurement. + + \list + \o Locale.MetricSystem This value indicates metric units, such as meters, + centimeters and millimeters. + \o Locale.ImperialSystem This value indicates imperial units, such as inches and + miles. There are several distinct imperial systems in the world; this + value stands for the official United States imperial units. + \endlist +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h new file mode 100644 index 0000000000..2763ce4fc3 --- /dev/null +++ b/src/qml/qml/qqmllocale_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLOCALE_H +#define QQMLLOCALE_H + +#include <qqml.h> + +#include <QtCore/qlocale.h> +#include <QtCore/qobject.h> +#include <private/qv8engine_p.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlDateExtension +{ +public: + static void registerExtension(QV8Engine *engine); + +private: + static v8::Handle<v8::Value> toLocaleString(const v8::Arguments& args); + static v8::Handle<v8::Value> toLocaleTimeString(const v8::Arguments& args); + static v8::Handle<v8::Value> toLocaleDateString(const v8::Arguments& args); + static v8::Handle<v8::Value> fromLocaleString(const v8::Arguments& args); + static v8::Handle<v8::Value> fromLocaleTimeString(const v8::Arguments& args); + static v8::Handle<v8::Value> fromLocaleDateString(const v8::Arguments& args); +}; + + +class QQmlNumberExtension +{ +public: + static void registerExtension(QV8Engine *engine); + +private: + static v8::Handle<v8::Value> toLocaleString(const v8::Arguments& args); + static v8::Handle<v8::Value> fromLocaleString(const v8::Arguments& args); + static v8::Handle<v8::Value> toLocaleCurrencyString(const v8::Arguments& args); +}; + + +class Q_AUTOTEST_EXPORT QQmlLocale +{ + Q_GADGET + Q_ENUMS(MeasurementSystem) + Q_ENUMS(FormatType) + Q_ENUMS(CurrencySymbolFormat) + Q_ENUMS(DayOfWeek) + +public: + ~QQmlLocale(); + + enum MeasurementSystem { + MetricSystem = QLocale::MetricSystem, + ImperialSystem = QLocale::ImperialSystem + }; + enum FormatType { + LongFormat = QLocale::LongFormat, + ShortFormat = QLocale::ShortFormat, + NarrowFormat = QLocale::NarrowFormat + }; + enum CurrencySymbolFormat { + CurrencyIsoCode = QLocale::CurrencyIsoCode, + CurrencySymbol = QLocale::CurrencySymbol, + CurrencyDisplayName = QLocale::CurrencyDisplayName + }; + // Qt defines Sunday as 7, but JS Date assigns Sunday 0 + enum DayOfWeek { + Sunday = 0, + Monday = Qt::Monday, + Tuesday = Qt::Tuesday, + Wednesday = Qt::Wednesday, + Thursday = Qt::Thursday, + Friday = Qt::Friday, + Saturday = Qt::Saturday + }; + + static v8::Handle<v8::Value> locale(QV8Engine *v8engine, const QString &lang); + + static void registerStringLocaleCompare(QV8Engine *engine); + +private: + QQmlLocale(); + + static v8::Handle<v8::Value> localeCompare(const v8::Arguments &args); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp new file mode 100644 index 0000000000..2061530dc5 --- /dev/null +++ b/src/qml/qml/qqmlmetatype.cpp @@ -0,0 +1,1359 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqmlprivate.h> +#include "qqmlmetatype_p.h" + +#include <private/qqmlproxymetaobject_p.h> +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlguard_p.h> +#include <private/qhashedstring_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 <qmetatype.h> +#include <qobjectdefs.h> +#include <qbytearray.h> +#include <qreadwritelock.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvector.h> + +#include <ctype.h> + +QT_BEGIN_NAMESPACE + +struct QQmlMetaTypeData +{ + QQmlMetaTypeData(); + ~QQmlMetaTypeData(); + QList<QQmlType *> types; + typedef QHash<int, QQmlType *> Ids; + Ids idToType; + typedef QHash<QString, QQmlType *> Names; + Names nameToType; + typedef QHash<const QMetaObject *, QQmlType *> MetaObjects; + MetaObjects metaObjectToType; + typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; + StringConverters stringConverters; + + struct VersionedUri { + VersionedUri() + : majorVersion(0) {} + VersionedUri(const QString &uri, int majorVersion) + : uri(uri), majorVersion(majorVersion) {} + bool operator==(const VersionedUri &other) const { + return other.majorVersion == majorVersion && other.uri == uri; + } + QString uri; + int majorVersion; + }; + typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules; + TypeModules uriToModule; + + struct ModuleApiList { + ModuleApiList() : sorted(true) {} + QList<QQmlMetaType::ModuleApi> moduleApis; + bool sorted; + }; + typedef QHash<QString, ModuleApiList> ModuleApis; + ModuleApis moduleApis; + int moduleApiCount; + + QBitArray objects; + QBitArray interfaces; + QBitArray lists; + + QList<QQmlPrivate::AutoParentFunction> parentFunctions; +}; + +class QQmlTypeModulePrivate +{ +public: + QQmlTypeModulePrivate() + : minMinorVersion(INT_MAX), maxMinorVersion(0) {} + + QQmlMetaTypeData::VersionedUri uri; + + int minMinorVersion; + int maxMinorVersion; + + void add(QQmlType *); + + QStringHash<QList<QQmlType *> > typeHash; + QList<QQmlType *> types; +}; + +Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) +Q_GLOBAL_STATIC(QReadWriteLock, metaTypeDataLock) + +static uint qHash(const QQmlMetaTypeData::VersionedUri &v) +{ + return qHash(v.uri) ^ qHash(v.majorVersion); +} + +QQmlMetaTypeData::QQmlMetaTypeData() +: moduleApiCount(0) +{ +} + +QQmlMetaTypeData::~QQmlMetaTypeData() +{ + for (int i = 0; i < types.count(); ++i) + delete types.at(i); +} + +class QQmlTypePrivate +{ +public: + QQmlTypePrivate(); + + void init() const; + void initEnums() const; + + bool m_isInterface : 1; + const char *m_iid; + QString m_module; + QString m_name; + QString m_elementName; + int m_version_maj; + int m_version_min; + int m_typeId; int m_listId; + int m_revision; + mutable bool m_containsRevisionedAttributes; + mutable QQmlType *m_superType; + + int m_allocationSize; + void (*m_newFunc)(void *); + QString m_noCreationReason; + + const QMetaObject *m_baseMetaObject; + QQmlAttachedPropertiesFunc m_attachedPropertiesFunc; + const QMetaObject *m_attachedPropertiesType; + int m_attachedPropertiesId; + int m_parserStatusCast; + int m_propertyValueSourceCast; + int m_propertyValueInterceptorCast; + QObject *(*m_extFunc)(QObject *); + const QMetaObject *m_extMetaObject; + int m_index; + QQmlCustomParser *m_customParser; + mutable volatile bool m_isSetup:1; + mutable volatile bool m_isEnumSetup:1; + mutable bool m_haveSuperType:1; + mutable QList<QQmlProxyMetaObject::ProxyData> m_metaObjects; + mutable QStringHash<int> m_enums; + + static QHash<const QMetaObject *, int> m_attachedPropertyIds; +}; + +QHash<const QMetaObject *, int> QQmlTypePrivate::m_attachedPropertyIds; + +QQmlTypePrivate::QQmlTypePrivate() +: m_isInterface(false), m_iid(0), m_typeId(0), m_listId(0), m_revision(0), m_containsRevisionedAttributes(false), + m_superType(0), m_allocationSize(0), m_newFunc(0), m_baseMetaObject(0), m_attachedPropertiesFunc(0), + m_attachedPropertiesType(0), m_parserStatusCast(-1), m_propertyValueSourceCast(-1), + m_propertyValueInterceptorCast(-1), m_extFunc(0), m_extMetaObject(0), m_index(-1), m_customParser(0), + m_isSetup(false), m_isEnumSetup(false), m_haveSuperType(false) +{ +} + + +QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) +: d(new QQmlTypePrivate) +{ + d->m_isInterface = true; + d->m_iid = interface.iid; + d->m_typeId = interface.typeId; + d->m_listId = interface.listId; + d->m_newFunc = 0; + d->m_index = index; + d->m_isSetup = true; + d->m_version_maj = 0; + d->m_version_min = 0; +} + +QQmlType::QQmlType(int index, const QQmlPrivate::RegisterType &type) +: d(new QQmlTypePrivate) +{ + QString name = QString::fromUtf8(type.uri); + if (type.uri) name += QLatin1Char('/'); + name += QString::fromUtf8(type.elementName); + + d->m_module = QString::fromUtf8(type.uri); + d->m_name = name; + d->m_version_maj = type.versionMajor; + d->m_version_min = type.versionMinor; + if (type.version >= 1) // revisions added in version 1 + d->m_revision = type.revision; + d->m_typeId = type.typeId; + d->m_listId = type.listId; + d->m_allocationSize = type.objectSize; + d->m_newFunc = type.create; + d->m_noCreationReason = type.noCreationReason; + d->m_baseMetaObject = type.metaObject; + d->m_attachedPropertiesFunc = type.attachedPropertiesFunction; + d->m_attachedPropertiesType = type.attachedPropertiesMetaObject; + if (d->m_attachedPropertiesType) { + QHash<const QMetaObject *, int>::Iterator iter = d->m_attachedPropertyIds.find(d->m_baseMetaObject); + if (iter == d->m_attachedPropertyIds.end()) + iter = d->m_attachedPropertyIds.insert(d->m_baseMetaObject, index); + d->m_attachedPropertiesId = *iter; + } else { + d->m_attachedPropertiesId = -1; + } + d->m_parserStatusCast = type.parserStatusCast; + d->m_propertyValueSourceCast = type.valueSourceCast; + d->m_propertyValueInterceptorCast = type.valueInterceptorCast; + d->m_extFunc = type.extensionObjectCreate; + d->m_index = index; + d->m_customParser = type.customParser; + + if (type.extensionMetaObject) + d->m_extMetaObject = type.extensionMetaObject; +} + +QQmlType::~QQmlType() +{ + delete d->m_customParser; + delete d; +} + +QString QQmlType::module() const +{ + return d->m_module; +} + +int QQmlType::majorVersion() const +{ + return d->m_version_maj; +} + +int QQmlType::minorVersion() const +{ + return d->m_version_min; +} + +bool QQmlType::availableInVersion(int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + return vmajor == d->m_version_maj && vminor >= d->m_version_min; +} + +bool QQmlType::availableInVersion(const QString &module, int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + return module == d->m_module && vmajor == d->m_version_maj && vminor >= d->m_version_min; +} + +// returns the nearest _registered_ super class +QQmlType *QQmlType::superType() const +{ + if (!d->m_haveSuperType) { + const QMetaObject *mo = d->m_baseMetaObject->superClass(); + while (mo && !d->m_superType) { + d->m_superType = QQmlMetaType::qmlType(mo, d->m_module, d->m_version_maj, d->m_version_min); + mo = mo->superClass(); + } + d->m_haveSuperType = true; + } + + return d->m_superType; +} + +static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) +{ + // Set classname + builder.setClassName(ignoreEnd->className()); + + // Clone Q_CLASSINFO + for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { + QMetaClassInfo info = mo->classInfo(ii); + + int otherIndex = ignoreEnd->indexOfClassInfo(info.name()); + if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { + // Skip + } else { + builder.addClassInfo(info.name(), info.value()); + } + } + + // Clone Q_PROPERTY + for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { + QMetaProperty property = mo->property(ii); + + int otherIndex = ignoreEnd->indexOfProperty(property.name()); + if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { + builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); + // Skip + } else { + builder.addProperty(property); + } + } + + // Clone Q_METHODS + for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { + QMetaMethod method = mo->method(ii); + + // More complex - need to search name + QByteArray name = method.signature(); + int parenIdx = name.indexOf('('); + if (parenIdx != -1) name = name.left(parenIdx); + + + bool found = false; + + for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); + !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); + ++ii) { + + QMetaMethod other = ignoreEnd->method(ii); + QByteArray othername = other.signature(); + int parenIdx = othername.indexOf('('); + if (parenIdx != -1) othername = othername.left(parenIdx); + + found = name == othername; + } + + QMetaMethodBuilder m = builder.addMethod(method); + if (found) // SKIP + m.setAccess(QMetaMethod::Private); + } + + // Clone Q_ENUMS + for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { + QMetaEnum enumerator = mo->enumerator(ii); + + int otherIndex = ignoreEnd->indexOfEnumerator(enumerator.name()); + if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { + // Skip + } else { + builder.addEnumerator(enumerator); + } + } +} + +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 (m_isSetup) return; + + QWriteLocker lock(metaTypeDataLock()); + if (m_isSetup) + return; + + // Setup extended meta object + // XXX - very inefficient + const QMetaObject *mo = m_baseMetaObject; + if (m_extFunc) { + QMetaObjectBuilder builder; + clone(builder, m_extMetaObject, m_extMetaObject, m_extMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = mo; + QQmlProxyMetaObject::ProxyData data = { mmo, m_extFunc, 0, 0 }; + m_metaObjects << data; + } + + mo = mo->d.superdata; + while(mo) { + QQmlType *t = metaTypeData()->metaObjectToType.value(mo); + if (t) { + if (t->d->m_extFunc) { + QMetaObjectBuilder builder; + clone(builder, t->d->m_extMetaObject, t->d->m_baseMetaObject, m_baseMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = m_baseMetaObject; + if (!m_metaObjects.isEmpty()) + m_metaObjects.last().metaObject->d.superdata = mmo; + QQmlProxyMetaObject::ProxyData data = { mmo, t->d->m_extFunc, 0, 0 }; + m_metaObjects << data; + } + } + mo = mo->d.superdata; + } + + for (int ii = 0; ii < m_metaObjects.count(); ++ii) { + m_metaObjects[ii].propertyOffset = + m_metaObjects.at(ii).metaObject->propertyOffset(); + m_metaObjects[ii].methodOffset = + m_metaObjects.at(ii).metaObject->methodOffset(); + } + + // Check for revisioned details + { + const QMetaObject *mo = 0; + if (m_metaObjects.isEmpty()) + mo = m_baseMetaObject; + else + mo = m_metaObjects.first().metaObject; + + for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { + if (isPropertyRevisioned(mo, ii)) + m_containsRevisionedAttributes = true; + } + + for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { + if (mo->method(ii).revision() != 0) + m_containsRevisionedAttributes = true; + } + } + + m_isSetup = true; + lock.unlock(); +} + +void QQmlTypePrivate::initEnums() const +{ + if (m_isEnumSetup) return; + + init(); + + QWriteLocker lock(metaTypeDataLock()); + if (m_isEnumSetup) return; + + const QMetaObject *metaObject = m_baseMetaObject; + for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { + + QMetaEnum e = metaObject->enumerator(ii); + + for (int jj = 0; jj < e.keyCount(); ++jj) + m_enums.insert(QString::fromUtf8(e.key(jj)), e.value(jj)); + } + + m_isEnumSetup = true; +} + +QByteArray QQmlType::typeName() const +{ + if (d->m_baseMetaObject) + return d->m_baseMetaObject->className(); + else + return QByteArray(); +} + +const QString &QQmlType::elementName() const +{ + if (d->m_elementName.isEmpty()) { + QString n = qmlTypeName(); + int idx = n.lastIndexOf(QLatin1Char('/')); + d->m_elementName = n.mid(idx + 1); + } + return d->m_elementName; +} + +const QString &QQmlType::qmlTypeName() const +{ + return d->m_name; +} + +QObject *QQmlType::create() const +{ + d->init(); + + QObject *rv = (QObject *)operator new(d->m_allocationSize); + d->m_newFunc(rv); + + if (rv && !d->m_metaObjects.isEmpty()) + (void *)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + + return rv; +} + +void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const +{ + d->init(); + + QObject *rv = (QObject *)operator new(d->m_allocationSize + additionalMemory); + d->m_newFunc(rv); + + if (rv && !d->m_metaObjects.isEmpty()) + (void *)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + + *out = rv; + *memory = ((char *)rv) + d->m_allocationSize; +} + +QQmlCustomParser *QQmlType::customParser() const +{ + return d->m_customParser; +} + +QQmlType::CreateFunc QQmlType::createFunction() const +{ + return d->m_newFunc; +} + +QString QQmlType::noCreationReason() const +{ + return d->m_noCreationReason; +} + +int QQmlType::createSize() const +{ + return d->m_allocationSize; +} + +bool QQmlType::isCreatable() const +{ + return d->m_newFunc != 0; +} + +bool QQmlType::isExtendedType() const +{ + d->init(); + + return !d->m_metaObjects.isEmpty(); +} + +bool QQmlType::isInterface() const +{ + return d->m_isInterface; +} + +int QQmlType::typeId() const +{ + return d->m_typeId; +} + +int QQmlType::qListTypeId() const +{ + return d->m_listId; +} + +const QMetaObject *QQmlType::metaObject() const +{ + d->init(); + + if (d->m_metaObjects.isEmpty()) + return d->m_baseMetaObject; + else + return d->m_metaObjects.first().metaObject; + +} + +const QMetaObject *QQmlType::baseMetaObject() const +{ + return d->m_baseMetaObject; +} + +bool QQmlType::containsRevisionedAttributes() const +{ + d->init(); + + return d->m_containsRevisionedAttributes; +} + +int QQmlType::metaObjectRevision() const +{ + return d->m_revision; +} + +QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction() const +{ + return d->m_attachedPropertiesFunc; +} + +const QMetaObject *QQmlType::attachedPropertiesType() const +{ + return d->m_attachedPropertiesType; +} + +/* +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() const +{ + return d->m_attachedPropertiesId; +} + +int QQmlType::parserStatusCast() const +{ + return d->m_parserStatusCast; +} + +int QQmlType::propertyValueSourceCast() const +{ + return d->m_propertyValueSourceCast; +} + +int QQmlType::propertyValueInterceptorCast() const +{ + return d->m_propertyValueInterceptorCast; +} + +const char *QQmlType::interfaceIId() const +{ + return d->m_iid; +} + +int QQmlType::index() const +{ + return d->m_index; +} + +int QQmlType::enumValue(const QHashedStringRef &name) const +{ + d->initEnums(); + + int *rv = d->m_enums.value(name); + return rv?*rv:-1; +} + +int QQmlType::enumValue(const QHashedV8String &name) const +{ + d->initEnums(); + + int *rv = d->m_enums.value(name); + return rv?*rv:-1; +} + +QQmlTypeModule::QQmlTypeModule() +: d(new QQmlTypeModulePrivate) +{ +} + +QQmlTypeModule::~QQmlTypeModule() +{ + delete d; d = 0; +} + +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(QQmlType *type) +{ + types << type; + + minMinorVersion = qMin(minMinorVersion, type->minorVersion()); + maxMinorVersion = qMax(maxMinorVersion, type->minorVersion()); + + QList<QQmlType *> &list = typeHash[type->elementName()]; + for (int ii = 0; ii < list.count(); ++ii) { + if (list.at(ii)->minorVersion() < type->minorVersion()) { + list.insert(ii, type); + return; + } + } + list.append(type); +} + +QList<QQmlType *> QQmlTypeModule::types() +{ + QList<QQmlType *> rv; + QReadLocker lock(metaTypeDataLock()); + rv = d->types; + return rv; +} + +QList<QQmlType *> QQmlTypeModule::type(const QString &name) +{ + QReadLocker lock(metaTypeDataLock()); + QList<QQmlType *> rv; + for (int ii = 0; ii < d->types.count(); ++ii) { + if (d->types.at(ii)->elementName() == name) + rv << d->types.at(ii); + } + return rv; +} + +QQmlType *QQmlTypeModule::type(const QHashedStringRef &name, int minor) +{ + QReadLocker lock(metaTypeDataLock()); + + QList<QQmlType *> *types = d->typeHash.value(name); + if (!types) return 0; + + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->minorVersion() <= minor) + return types->at(ii); + + return 0; +} + +QQmlType *QQmlTypeModule::type(const QHashedV8String &name, int minor) +{ + QReadLocker lock(metaTypeDataLock()); + + QList<QQmlType *> *types = d->typeHash.value(name); + if (!types) return 0; + + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->minorVersion() <= minor) + return types->at(ii); + + return 0; +} + + +QQmlTypeModuleVersion::QQmlTypeModuleVersion() +: m_module(0), 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 m_module->type(name, m_minor); + else return 0; +} + +QQmlType *QQmlTypeModuleVersion::type(const QHashedV8String &name) const +{ + if (m_module) return m_module->type(name, m_minor); + else return 0; +} + + +int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) +{ + QWriteLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + data->parentFunctions.append(autoparent.function); + + return data->parentFunctions.count() - 1; +} + +int registerInterface(const QQmlPrivate::RegisterInterface &interface) +{ + if (interface.version > 0) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + + QWriteLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + int index = data->types.count(); + + QQmlType *type = new QQmlType(index, interface); + + data->types.append(type); + data->idToType.insert(type->typeId(), type); + data->idToType.insert(type->qListTypeId(), type); + // XXX No insertMulti, so no multi-version interfaces? + if (!type->qmlTypeName().isEmpty()) + data->nameToType.insert(type->qmlTypeName(), type); + + 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); + + return index; +} + +int registerType(const QQmlPrivate::RegisterType &type) +{ + if (type.elementName) { + for (int ii = 0; type.elementName[ii]; ++ii) { + if (!isalnum(type.elementName[ii])) { + qWarning("qmlRegisterType(): Invalid QML element name \"%s\"", type.elementName); + return -1; + } + } + } + + QWriteLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + int index = data->types.count(); + + QQmlType *dtype = new QQmlType(index, type); + + data->types.append(dtype); + data->idToType.insert(dtype->typeId(), dtype); + if (dtype->qListTypeId()) data->idToType.insert(dtype->qListTypeId(), dtype); + + if (!dtype->qmlTypeName().isEmpty()) + data->nameToType.insertMulti(dtype->qmlTypeName(), dtype); + + data->metaObjectToType.insertMulti(dtype->baseMetaObject(), dtype); + + if (data->objects.size() <= type.typeId) + data->objects.resize(type.typeId + 16); + if (data->lists.size() <= type.listId) + data->lists.resize(type.listId + 16); + data->objects.setBit(type.typeId, true); + if (type.listId) data->lists.setBit(type.listId, true); + + if (type.uri) { + QString mod = QString::fromUtf8(type.uri); + + QQmlMetaTypeData::VersionedUri versionedUri(mod, type.versionMajor); + QQmlTypeModule *module = data->uriToModule.value(versionedUri); + if (!module) { + module = new QQmlTypeModule; + module->d->uri = versionedUri; + data->uriToModule.insert(versionedUri, module); + } + module->d->add(dtype); + } + + return index; +} + +int registerModuleApi(const QQmlPrivate::RegisterModuleApi &api) +{ + QWriteLocker lock(metaTypeDataLock()); + + QQmlMetaTypeData *data = metaTypeData(); + QString uri = QString::fromUtf8(api.uri); + QQmlMetaType::ModuleApi import; + import.major = api.versionMajor; + import.minor = api.versionMinor; + import.script = api.scriptApi; + import.qobject = api.qobjectApi; + + int index = data->moduleApiCount++; + + QQmlMetaTypeData::ModuleApis::Iterator iter = data->moduleApis.find(uri); + if (iter == data->moduleApis.end()) { + QQmlMetaTypeData::ModuleApiList apis; + apis.moduleApis << import; + data->moduleApis.insert(uri, apis); + } else { + iter->moduleApis << import; + iter->sorted = false; + } + + return index; +} + + +/* +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 == TypeRegistration) { + return registerType(*reinterpret_cast<RegisterType *>(data)); + } else if (type == InterfaceRegistration) { + return registerInterface(*reinterpret_cast<RegisterInterface *>(data)); + } else if (type == AutoParentRegistration) { + return registerAutoParentFunction(*reinterpret_cast<RegisterAutoParent *>(data)); + } else if (type == ModuleApiRegistration) { + return registerModuleApi(*reinterpret_cast<RegisterModuleApi *>(data)); + } + return -1; +} + +/* + Returns true if a module \a uri of any version is installed. +*/ +bool QQmlMetaType::isAnyModule(const QString &uri) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.begin(); + iter != data->uriToModule.end(); ++iter) { + if ((*iter)->module() == uri) + return true; + } + + return false; +} + +/* + Returns true if any type or API has been registered for the given \a module with at least + versionMajor.versionMinor, or if types have been registered for \a module with at most + versionMajor.versionMinor. + + So if only 4.7 and 4.9 have been registered, 4.7,4.8, and 4.9 are valid, but not 4.6 nor 4.10. +*/ +bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor) +{ + Q_ASSERT(versionMajor >= 0 && versionMinor >= 0); + QReadLocker lock(metaTypeDataLock()); + + QQmlMetaTypeData *data = metaTypeData(); + + // first, check Types + QQmlTypeModule *tm = + data->uriToModule.value(QQmlMetaTypeData::VersionedUri(module, versionMajor)); + if (tm && tm->minimumMinorVersion() <= versionMinor && tm->maximumMinorVersion() >= versionMinor) + return true; + + // then, check ModuleApis + foreach (const QQmlMetaType::ModuleApi &mApi, data->moduleApis.value(module).moduleApis) { + if (mApi.major == versionMajor && mApi.minor == versionMinor) // XXX is this correct? + return true; + } + + return false; +} + +QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return data->uriToModule.value(QQmlMetaTypeData::VersionedUri(uri, majorVersion)); +} + +QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return data->parentFunctions; +} + +static bool operator<(const QQmlMetaType::ModuleApi &lhs, const QQmlMetaType::ModuleApi &rhs) +{ + return lhs.major < rhs.major || (lhs.major == rhs.major && lhs.minor < rhs.minor); +} + +QQmlMetaType::ModuleApi +QQmlMetaType::moduleApi(const QString &uri, int versionMajor, int versionMinor) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlMetaTypeData::ModuleApis::Iterator iter = data->moduleApis.find(uri); + if (iter == data->moduleApis.end()) + return ModuleApi(); + + if (iter->sorted == false) { + qSort(iter->moduleApis.begin(), iter->moduleApis.end()); + iter->sorted = true; + } + + for (int ii = iter->moduleApis.count() - 1; ii >= 0; --ii) { + const ModuleApi &import = iter->moduleApis.at(ii); + if (import.major == versionMajor && import.minor <= versionMinor) + return import; + } + + return ModuleApi(); +} + +QHash<QString, QList<QQmlMetaType::ModuleApi> > QQmlMetaType::moduleApis() +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QHash<QString, QList<ModuleApi> > moduleApis; + QHashIterator<QString, QQmlMetaTypeData::ModuleApiList> it(data->moduleApis); + while (it.hasNext()) { + it.next(); + moduleApis[it.key()] = it.value().moduleApis; + } + + return moduleApis; +} + +QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) +{ + if (!isQObject(v.userType())) { + if (ok) *ok = false; + return 0; + } + + if (ok) *ok = true; + + return *(QObject **)v.constData(); +} + +bool QQmlMetaType::isQObject(int userType) +{ + if (userType == QMetaType::QObjectStar) + return true; + + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); +} + +/* + Returns the item type for a list of type \a id. + */ +int QQmlMetaType::listType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + QQmlType *type = data->idToType.value(id); + if (type && type->qListTypeId() == id) + return type->typeId(); + else + return 0; +} + +int QQmlMetaType::attachedPropertiesFuncId(const QMetaObject *mo) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlType *type = data->metaObjectToType.value(mo); + if (type && type->attachedPropertiesFunction()) + return type->attachedPropertiesId(); + else + return -1; +} + +QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(int id) +{ + if (id < 0) + return 0; + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return data->types.at(id)->attachedPropertiesFunction(); +} + +QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultProperty"); + if (-1 == idx) + return QMetaProperty(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if (!info.value()) + return QMetaProperty(); + + idx = metaObject->indexOfProperty(info.value()); + if (-1 == idx) + return QMetaProperty(); + + return metaObject->property(idx); +} + +QMetaProperty QQmlMetaType::defaultProperty(QObject *obj) +{ + if (!obj) + return QMetaProperty(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultProperty(metaObject); +} + +QMetaMethod QQmlMetaType::defaultMethod(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultMethod"); + if (-1 == idx) + return QMetaMethod(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if (!info.value()) + return QMetaMethod(); + + idx = metaObject->indexOfMethod(info.value()); + if (-1 == idx) + return QMetaMethod(); + + return metaObject->method(idx); +} + +QMetaMethod QQmlMetaType::defaultMethod(QObject *obj) +{ + if (!obj) + return QMetaMethod(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultMethod(metaObject); +} + +QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) +{ + if (userType < 0) + return Unknown; + if (userType == QMetaType::QObjectStar) + return Object; + + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + if (userType < data->objects.size() && data->objects.testBit(userType)) + return Object; + else if (userType < data->lists.size() && data->lists.testBit(userType)) + return List; + else + return Unknown; +} + +bool QQmlMetaType::isInterface(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); +} + +const char *QQmlMetaType::interfaceIId(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + QQmlType *type = data->idToType.value(userType); + lock.unlock(); + if (type && type->isInterface() && type->typeId() == userType) + return type->interfaceIId(); + else + return 0; +} + +bool QQmlMetaType::isList(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); +} + +/*! + A custom string convertor allows you to specify a function pointer that + returns a variant of \a type. For example, if you have written your own icon + class that you want to support as an object property assignable in QML: + + \code + int type = qRegisterMetaType<SuperIcon>("SuperIcon"); + QML::addCustomStringConvertor(type, &SuperIcon::pixmapFromString); + \endcode + + The function pointer must be of the form: + \code + QVariant (*StringConverter)(const QString &); + \endcode + */ +void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter) +{ + QWriteLocker lock(metaTypeDataLock()); + + QQmlMetaTypeData *data = metaTypeData(); + if (data->stringConverters.contains(type)) + return; + data->stringConverters.insert(type, converter); +} + +/*! + Return the custom string converter for \a type, previously installed through + registerCustomStringConverter() + */ +QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) +{ + QReadLocker lock(metaTypeDataLock()); + + QQmlMetaTypeData *data = metaTypeData(); + return data->stringConverters.value(type); +} + +/*! + Returns the type (if any) of URI-qualified named \a name in version specified + by \a version_major and \a version_minor. +*/ +QQmlType *QQmlMetaType::qmlType(const QString &name, int version_major, int version_minor) +{ + Q_ASSERT(version_major >= 0 && version_minor >= 0); + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.find(name); + while (it != data->nameToType.end()) { + // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty + if (it.key() == name && (version_major<0 || (*it)->availableInVersion(version_major,version_minor))) + return (*it); + ++it; + } + + return 0; +} + +/*! + Returns the type (if any) that corresponds to the \a metaObject. Returns null if no + type is registered. +*/ +QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + return data->metaObjectToType.value(metaObject); +} + +/*! + Returns the type (if any) that corresponds to the \a metaObject in version specified + by \a version_major and \a version_minor in module specified by \a uri. Returns null if no + type is registered. +*/ +QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QString &module, int version_major, int version_minor) +{ + Q_ASSERT(version_major >= 0 && version_minor >= 0); + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.find(metaObject); + while (it != data->metaObjectToType.end() && it.key() == metaObject) { + QQmlType *t = *it; + if (version_major < 0 || t->availableInVersion(module, version_major,version_minor)) + return t; + ++it; + } + + return 0; +} + +/*! + Returns the type (if any) that corresponds to the QVariant::Type \a userType. + Returns null if no type is registered. +*/ +QQmlType *QQmlMetaType::qmlType(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlType *type = data->idToType.value(userType); + if (type && type->typeId() == userType) + return type; + else + return 0; +} + +/*! + Returns the list of registered QML type names. +*/ +QList<QString> QQmlMetaType::qmlTypeNames() +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + return data->nameToType.keys(); +} + +/*! + Returns the list of registered QML types. +*/ +QList<QQmlType*> QQmlMetaType::qmlTypes() +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + return data->nameToType.values(); +} + +int QQmlMetaType::QQuickAnchorLineMetaTypeId() +{ + static int id = 0; + if (!id) { + id = QMetaType::type("QQuickAnchorLine"); + } + return id; +} + +QQmlMetaType::CompareFunction QQmlMetaType::anchorLineCompareFunction = 0; + +void QQmlMetaType::setQQuickAnchorLineCompareFunction(CompareFunction fun) +{ + anchorLineCompareFunction = fun; +} + +bool QQmlMetaType::QQuickAnchorLineCompare(const void *p1, const void *p2) +{ + Q_ASSERT(anchorLineCompareFunction != 0); + return anchorLineCompareFunction(p1, p2); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h new file mode 100644 index 0000000000..ad6a2aa055 --- /dev/null +++ b/src/qml/qml/qqmlmetatype_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMETATYPE_P_H +#define QQMLMETATYPE_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 "qqml.h" +#include <private/qtqmlglobal_p.h> + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <QtCore/qbitarray.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_NAMESPACE + +class QQmlType; +class QQmlCustomParser; +class QQmlTypePrivate; +class QQmlTypeModule; + +class Q_QML_PRIVATE_EXPORT QQmlMetaType +{ +public: + static QList<QString> qmlTypeNames(); + static QList<QQmlType*> qmlTypes(); + + static QQmlType *qmlType(const QString &, int, int); + static QQmlType *qmlType(const QMetaObject *); + static QQmlType *qmlType(const QMetaObject *metaObject, const QString &module, int version_major, int version_minor); + static QQmlType *qmlType(int); + + static QMetaProperty defaultProperty(const QMetaObject *); + static QMetaProperty defaultProperty(QObject *); + static QMetaMethod defaultMethod(const QMetaObject *); + static QMetaMethod defaultMethod(QObject *); + + static bool isQObject(int); + static QObject *toQObject(const QVariant &, bool *ok = 0); + + static int listType(int); + static int attachedPropertiesFuncId(const QMetaObject *); + static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(int); + + enum TypeCategory { Unknown, Object, List }; + static TypeCategory typeCategory(int); + + static bool isInterface(int); + static const char *interfaceIId(int); + static bool isList(int); + + typedef QVariant (*StringConverter)(const QString &); + static void registerCustomStringConverter(int, StringConverter); + static StringConverter customStringConverter(int); + + static bool isAnyModule(const QString &uri); + static bool isModule(const QString &module, int versionMajor, int versionMinor); + static QQmlTypeModule *typeModule(const QString &uri, int majorVersion); + + static QList<QQmlPrivate::AutoParentFunction> parentFunctions(); + + static int QQuickAnchorLineMetaTypeId(); + typedef bool (*CompareFunction)(const void *, const void *); + static void setQQuickAnchorLineCompareFunction(CompareFunction); + static bool QQuickAnchorLineCompare(const void *p1, const void *p2); + + struct ModuleApiInstance { + ModuleApiInstance() + : scriptCallback(0), qobjectCallback(0), qobjectApi(0) {} + + QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *); + QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *); + QJSValue scriptApi; + QObject *qobjectApi; + }; + struct ModuleApi { + inline ModuleApi(); + inline bool operator==(const ModuleApi &) const; + int major; + int minor; + QJSValue (*script)(QQmlEngine *, QJSEngine *); + QObject *(*qobject)(QQmlEngine *, QJSEngine *); + }; + static ModuleApi moduleApi(const QString &, int, int); + static QHash<QString, QList<ModuleApi> > moduleApis(); + +private: + static CompareFunction anchorLineCompareFunction; +}; + +class QHashedStringRef; +class QHashedV8String; +class Q_QML_PRIVATE_EXPORT QQmlType +{ +public: + QByteArray typeName() const; + const QString &qmlTypeName() const; + const QString &elementName() const; + + QString module() const; + int majorVersion() const; + int minorVersion() const; + + bool availableInVersion(int vmajor, int vminor) const; + bool availableInVersion(const QString &module, int vmajor, int vminor) const; + + QObject *create() const; + void create(QObject **, void **, size_t) const; + + typedef void (*CreateFunc)(void *); + CreateFunc createFunction() const; + int createSize() const; + + QQmlCustomParser *customParser() const; + + bool isCreatable() const; + bool isExtendedType() const; + QString noCreationReason() const; + + bool isInterface() const; + int typeId() const; + int qListTypeId() const; + + const QMetaObject *metaObject() const; + const QMetaObject *baseMetaObject() const; + int metaObjectRevision() const; + bool containsRevisionedAttributes() const; + + QQmlAttachedPropertiesFunc attachedPropertiesFunction() const; + const QMetaObject *attachedPropertiesType() const; + int attachedPropertiesId() const; + + int parserStatusCast() const; + QVariant fromObject(QObject *) const; + const char *interfaceIId() const; + int propertyValueSourceCast() const; + int propertyValueInterceptorCast() const; + + int index() const; + + int enumValue(const QHashedStringRef &) const; + int enumValue(const QHashedV8String &) const; +private: + QQmlType *superType() const; + friend class QQmlTypePrivate; + friend struct QQmlMetaTypeData; + friend int registerType(const QQmlPrivate::RegisterType &); + friend int registerInterface(const QQmlPrivate::RegisterInterface &); + QQmlType(int, const QQmlPrivate::RegisterInterface &); + QQmlType(int, const QQmlPrivate::RegisterType &); + ~QQmlType(); + + QQmlTypePrivate *d; +}; + +class QQmlTypeModulePrivate; +class QQmlTypeModule +{ +public: + QString module() const; + int majorVersion() const; + + int minimumMinorVersion() const; + int maximumMinorVersion() const; + + QList<QQmlType *> types(); + QList<QQmlType *> type(const QString &); + + QQmlType *type(const QHashedStringRef &, int); + QQmlType *type(const QHashedV8String &, int); + +private: + friend int registerType(const QQmlPrivate::RegisterType &); + 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 QHashedV8String &) const; + +private: + QQmlTypeModule *m_module; + int m_minor; +}; + +QQmlMetaType::ModuleApi::ModuleApi() +{ + major = 0; + minor = 0; + script = 0; + qobject = 0; +} + +bool QQmlMetaType::ModuleApi::operator==(const ModuleApi &other) const +{ + return major == other.major && minor == other.minor && script == other.script && qobject == other.qobject; +} + +inline uint qHash(const QQmlMetaType::ModuleApi &import) +{ + return import.major ^ import.minor ^ quintptr(import.script) ^ quintptr(import.qobject); +} + +QT_END_NAMESPACE + +#endif // QQMLMETATYPE_P_H + diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp new file mode 100644 index 0000000000..cc33f387d9 --- /dev/null +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlnetworkaccessmanagerfactory.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlNetworkAccessManagerFactory + \since 4.7 + \brief The QQmlNetworkAccessManagerFactory class creates QNetworkAccessManager instances for a QML engine. + + A QML engine uses QNetworkAccessManager for all network access. + By implementing a factory, it is possible to provide the QML engine + with custom QNetworkAccessManager instances with specialized caching, + proxy and cookies support. + + To implement a factory, subclass QQmlNetworkAccessManagerFactory and + implement the virtual create() method, then assign it to the relevant QML + engine using QQmlEngine::setNetworkAccessManagerFactory(). + + Note the QML engine may create QNetworkAccessManager instances + from multiple threads. Because of this, the implementation of the create() + method must be \l{Reentrancy and Thread-Safety}{reentrant}. In addition, + the developer should be careful if the signals of the object to be + returned from create() are connected to the slots of an object that may + be created in a different thread: + + \list + \o The QML engine internally handles all requests, and cleans up any + QNetworkReply objects it creates. Receiving the + QNetworkAccessManager::finished() signal in another thread may not + provide the receiver with a valid reply object if it has already + been deleted. + \o Authentication details provided to QNetworkAccessManager::authenticationRequired() + must be provided immediately, so this signal cannot be connected as a + Qt::QueuedConnection (or as the default Qt::AutoConnection from another + thread). + \endlist + + For more information about signals and threads, see + \l {Threads and QObjects} and \l {Signals and Slots Across Threads}. + + \sa {declarative/cppextensions/networkaccessmanagerfactory}{NetworkAccessManagerFactory example} +*/ + +/*! + Destroys the factory. The default implementation does nothing. + */ +QQmlNetworkAccessManagerFactory::~QQmlNetworkAccessManagerFactory() +{ +} + +/*! + \fn QNetworkAccessManager *QQmlNetworkAccessManagerFactory::create(QObject *parent) + + Creates and returns a network access manager with the specified \a parent. + This method must return a new QNetworkAccessManager instance each time + it is called. + + Note: this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h new file mode 100644 index 0000000000..ac3583cf4c --- /dev/null +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLNETWORKACCESSMANAGERFACTORY_H +#define QQMLNETWORKACCESSMANAGERFACTORY_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QNetworkAccessManager; +class Q_QML_EXPORT QQmlNetworkAccessManagerFactory +{ +public: + virtual ~QQmlNetworkAccessManagerFactory(); + virtual QNetworkAccessManager *create(QObject *parent) = 0; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLNETWORKACCESSMANAGERFACTORY_H diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp new file mode 100644 index 0000000000..270eee52b4 --- /dev/null +++ b/src/qml/qml/qqmlnotifier.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlnotifier_p.h" +#include "qqmlproperty_p.h" + +QT_BEGIN_NAMESPACE + +void QQmlNotifier::emitNotify(QQmlNotifierEndpoint *endpoint) +{ + QQmlNotifierEndpoint **oldDisconnected = endpoint->disconnected; + endpoint->disconnected = &endpoint; + endpoint->notifying = 1; + + if (endpoint->next) + emitNotify(endpoint->next); + + if (endpoint) { + + Q_ASSERT(endpoint->callback); + + endpoint->callback(endpoint); + + if (endpoint) + endpoint->disconnected = oldDisconnected; + } + + if (oldDisconnected) *oldDisconnected = endpoint; + else if (endpoint) endpoint->notifying = 0; +} + +void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal) +{ + disconnect(); + + this->source = source; + this->sourceSignal = sourceSignal; + QQmlPropertyPrivate::flushSignal(source, sourceSignal); + QQmlData *ddata = QQmlData::get(source, true); + ddata->addNotify(sourceSignal, this); +} + +void QQmlNotifierEndpoint::copyAndClear(QQmlNotifierEndpoint &other) +{ + if (&other == this) + return; + + other.disconnect(); + + other.callback = callback; + + if (!isConnected()) + return; + + other.notifier = notifier; + other.sourceSignal = sourceSignal; + other.disconnected = disconnected; + other.notifying = notifying; + if (other.disconnected) *other.disconnected = &other; + + if (next) { + other.next = next; + next->prev = &other.next; + } + other.prev = prev; + *other.prev = &other; + + prev = 0; + next = 0; + disconnected = 0; + notifier = 0; + notifying = 0; + sourceSignal = -1; +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h new file mode 100644 index 0000000000..ab0711341d --- /dev/null +++ b/src/qml/qml/qqmlnotifier_p.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLNOTIFIER_P_H +#define QQMLNOTIFIER_P_H + +#include "qqmldata_p.h" +#include "qqmlguard_p.h" + +QT_BEGIN_NAMESPACE + +class QQmlNotifierEndpoint; +class Q_QML_EXPORT QQmlNotifier +{ +public: + inline QQmlNotifier(); + inline ~QQmlNotifier(); + inline void notify(); + +private: + friend class QQmlData; + friend class QQmlNotifierEndpoint; + + static void emitNotify(QQmlNotifierEndpoint *); + QQmlNotifierEndpoint *endpoints; +}; + +class QQmlNotifierEndpoint +{ +public: + inline QQmlNotifierEndpoint(); + inline ~QQmlNotifierEndpoint(); + + typedef void (*Callback)(QQmlNotifierEndpoint *); + Callback callback; + + inline bool isConnected(); + inline bool isConnected(QObject *source, int sourceSignal); + inline bool isConnected(QQmlNotifier *); + + void connect(QObject *source, int sourceSignal); + inline void connect(QQmlNotifier *); + inline void disconnect(); + + inline bool isNotifying() const; + inline void cancelNotify(); + + void copyAndClear(QQmlNotifierEndpoint &other); + +private: + friend class QQmlData; + friend class QQmlNotifier; + + union { + QQmlNotifier *notifier; + QObject *source; + }; + unsigned int notifying : 1; + signed int sourceSignal : 31; + QQmlNotifierEndpoint **disconnected; + QQmlNotifierEndpoint *next; + QQmlNotifierEndpoint **prev; +}; + +QQmlNotifier::QQmlNotifier() +: endpoints(0) +{ +} + +QQmlNotifier::~QQmlNotifier() +{ + QQmlNotifierEndpoint *endpoint = endpoints; + while (endpoint) { + QQmlNotifierEndpoint *n = endpoint; + endpoint = n->next; + + n->next = 0; + n->prev = 0; + n->notifier = 0; + n->sourceSignal = -1; + if (n->disconnected) *n->disconnected = 0; + n->disconnected = 0; + } + endpoints = 0; +} + +void QQmlNotifier::notify() +{ + if (endpoints) emitNotify(endpoints); +} + +QQmlNotifierEndpoint::QQmlNotifierEndpoint() +: callback(0), notifier(0), notifying(0), sourceSignal(-1), disconnected(0), next(0), prev(0) +{ +} + +QQmlNotifierEndpoint::~QQmlNotifierEndpoint() +{ + disconnect(); +} + +bool QQmlNotifierEndpoint::isConnected() +{ + return prev != 0; +} + +bool QQmlNotifierEndpoint::isConnected(QObject *source, int sourceSignal) +{ + return this->sourceSignal != -1 && this->source == source && this->sourceSignal == sourceSignal; +} + +bool QQmlNotifierEndpoint::isConnected(QQmlNotifier *notifier) +{ + return sourceSignal == -1 && this->notifier == notifier; +} + +void QQmlNotifierEndpoint::connect(QQmlNotifier *notifier) +{ + disconnect(); + + next = notifier->endpoints; + if (next) { next->prev = &next; } + notifier->endpoints = this; + prev = ¬ifier->endpoints; + this->notifier = notifier; +} + +void QQmlNotifierEndpoint::disconnect() +{ + if (next) next->prev = prev; + if (prev) *prev = next; + if (disconnected) *disconnected = 0; + next = 0; + prev = 0; + disconnected = 0; + notifier = 0; + notifying = 0; + sourceSignal = -1; +} + +/*! +Returns true if a notify is in progress. This means that the signal or QQmlNotifier +that this endpoing is connected to has been triggered, but this endpoint's callback has not +yet been called. + +An in progress notify can be cancelled by calling cancelNotify. +*/ +bool QQmlNotifierEndpoint::isNotifying() const +{ + return notifying == 1; +} + +/*! +Cancel any notifies that are in progress. +*/ +void QQmlNotifierEndpoint::cancelNotify() +{ + notifying = 0; + if (disconnected) { + *disconnected = 0; + disconnected = 0; + } +} + +QT_END_NAMESPACE + +#endif // QQMLNOTIFIER_P_H + diff --git a/src/qml/qml/qqmlnullablevalue_p_p.h b/src/qml/qml/qqmlnullablevalue_p_p.h new file mode 100644 index 0000000000..b19e2722cf --- /dev/null +++ b/src/qml/qml/qqmlnullablevalue_p_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLNULLABLEVALUE_P_H +#define QQMLNULLABLEVALUE_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. +// + +QT_BEGIN_NAMESPACE + +template<typename T> +struct QQmlNullableValue +{ + QQmlNullableValue() + : isNull(true), value(T()) {} + QQmlNullableValue(const QQmlNullableValue<T> &o) + : isNull(o.isNull), value(o.value) {} + QQmlNullableValue(const T &t) + : isNull(false), value(t) {} + QQmlNullableValue<T> &operator=(const T &t) + { isNull = false; value = t; return *this; } + QQmlNullableValue<T> &operator=(const QQmlNullableValue<T> &o) + { isNull = o.isNull; value = o.value; return *this; } + operator T() const { return value; } + + void invalidate() { isNull = true; } + bool isValid() const { return !isNull; } + bool isNull; + T value; +}; + +QT_END_NAMESPACE + +#endif // QQMLNULLABLEVALUE_P_H diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp new file mode 100644 index 0000000000..221cb3a314 --- /dev/null +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlopenmetaobject_p.h" +#include <private/qqmlpropertycache_p.h> +#include <private/qqmldata_p.h> +#include <private/qmetaobjectbuilder_p.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + + +class QQmlOpenMetaObjectTypePrivate +{ +public: + QQmlOpenMetaObjectTypePrivate() : mem(0), cache(0), engine(0) {} + + void init(const QMetaObject *metaObj); + + int propertyOffset; + int signalOffset; + QHash<QByteArray, int> names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QQmlPropertyCache *cache; + QQmlEngine *engine; + QSet<QQmlOpenMetaObject*> referers; +}; + +QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine) + : QQmlCleanup(engine), d(new QQmlOpenMetaObjectTypePrivate) +{ + d->engine = engine; + d->init(base); +} + +QQmlOpenMetaObjectType::~QQmlOpenMetaObjectType() +{ + if (d->mem) + free(d->mem); + if (d->cache) + d->cache->release(); + delete d; +} + +void QQmlOpenMetaObjectType::clear() +{ + d->engine = 0; +} + +int QQmlOpenMetaObjectType::propertyOffset() const +{ + return d->propertyOffset; +} + +int QQmlOpenMetaObjectType::signalOffset() const +{ + return d->signalOffset; +} + +int QQmlOpenMetaObjectType::createProperty(const QByteArray &name) +{ + int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + propertyCreated(id, build); + free(d->mem); + d->mem = d->mob.toMetaObject(); + d->names.insert(name, id); + QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QQmlOpenMetaObject *omo = *it; + *static_cast<QMetaObject *>(omo) = *d->mem; + if (d->cache) + d->cache->update(d->engine, omo); + ++it; + } + + return d->propertyOffset + id; +} + +void QQmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) +{ + if (d->referers.count()) + (*d->referers.begin())->propertyCreated(id, builder); +} + +void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) +{ + if (!mem) { + mob.setSuperClass(metaObj); + mob.setClassName(metaObj->className()); + mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + mem = mob.toMetaObject(); + + propertyOffset = mem->propertyOffset(); + signalOffset = mem->methodOffset(); + } +} + +//---------------------------------------------------------------------------- + +class QQmlOpenMetaObjectPrivate +{ +public: + QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q) + : q(_q), parent(0), type(0), cacheProperties(false) {} + + inline QVariant &getData(int idx) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + if (!prop.second) { + prop.first = q->initialValue(idx); + prop.second = true; + } + return prop.first; + } + + inline void writeData(int idx, const QVariant &value) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + prop.first = value; + prop.second = true; + } + + inline bool hasData(int idx) const { + if (idx >= data.count()) + return false; + return data[idx].second; + } + + bool autoCreate; + QQmlOpenMetaObject *q; + QAbstractDynamicMetaObject *parent; + QList<QPair<QVariant, bool> > data; + QObject *object; + QQmlOpenMetaObjectType *type; + bool cacheProperties; +}; + +QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, bool automatic) +: d(new QQmlOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = new QQmlOpenMetaObjectType(obj->metaObject(), 0); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic) +: d(new QQmlOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = type; + d->type->addref(); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QQmlOpenMetaObject::~QQmlOpenMetaObject() +{ + if (d->parent) + delete d->parent; + d->type->d->referers.remove(this); + d->type->release(); + delete d; +} + +QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const +{ + return d->type; +} + +int QQmlOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) + && id >= d->type->d->propertyOffset) { + int propId = id - d->type->d->propertyOffset; + if (c == QMetaObject::ReadProperty) { + propertyRead(propId); + *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); + } else if (c == QMetaObject::WriteProperty) { + if (propId <= d->data.count() || d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { + propertyWrite(propId); + d->writeData(propId, *reinterpret_cast<QVariant *>(a[0])); + propertyWritten(propId); + activate(d->object, d->type->d->signalOffset + propId, 0); + } + } + return -1; + } else { + if (d->parent) + return d->parent->metaCall(c, id, a); + else + return d->object->qt_metacall(c, id, a); + } +} + +QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const +{ + return d->parent; +} + +QVariant QQmlOpenMetaObject::value(int id) const +{ + return d->getData(id); +} + +void QQmlOpenMetaObject::setValue(int id, const QVariant &value) +{ + d->writeData(id, value); + activate(d->object, id + d->type->d->signalOffset, 0); +} + +QVariant QQmlOpenMetaObject::value(const QByteArray &name) const +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + if (iter == d->type->d->names.end()) + return QVariant(); + + return d->getData(*iter); +} + +QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + Q_ASSERT(iter != d->type->d->names.end()); + + return d->getData(*iter); +} + +QVariant &QQmlOpenMetaObject::operator[](int id) +{ + return d->getData(id); +} + +bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + + int id = -1; + if (iter == d->type->d->names.end()) { + id = createProperty(name.constData(), "") - d->type->d->propertyOffset; + } else { + id = *iter; + } + + if (id >= 0) { + QVariant &dataVal = d->getData(id); + if (dataVal == val) + return false; + + dataVal = val; + activate(d->object, id + d->type->d->signalOffset, 0); + return true; + } + + return false; +} + +// returns true if this value has been initialized by a call to either value() or setValue() +bool QQmlOpenMetaObject::hasValue(int id) const +{ + return d->hasData(id); +} + +void QQmlOpenMetaObject::setCached(bool c) +{ + if (c == d->cacheProperties || !d->type->d->engine) + return; + + d->cacheProperties = c; + + QQmlData *qmldata = QQmlData::get(d->object, true); + if (d->cacheProperties) { + if (!d->type->d->cache) + d->type->d->cache = new QQmlPropertyCache(d->type->d->engine, this); + qmldata->propertyCache = d->type->d->cache; + d->type->d->cache->addref(); + } else { + if (d->type->d->cache) + d->type->d->cache->release(); + qmldata->propertyCache = 0; + } +} + + +int QQmlOpenMetaObject::createProperty(const char *name, const char *) +{ + if (d->autoCreate) + return d->type->createProperty(name); + else + return -1; +} + +void QQmlOpenMetaObject::propertyRead(int) +{ +} + +void QQmlOpenMetaObject::propertyWrite(int) +{ +} + +void QQmlOpenMetaObject::propertyWritten(int) +{ +} + +void QQmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ +} + +QVariant QQmlOpenMetaObject::initialValue(int) +{ + return QVariant(); +} + +int QQmlOpenMetaObject::count() const +{ + return d->type->d->names.count(); +} + +QByteArray QQmlOpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + + return d->type->d->mob.property(idx).name(); +} + +QObject *QQmlOpenMetaObject::object() const +{ + return d->object; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h new file mode 100644 index 0000000000..188192edc1 --- /dev/null +++ b/src/qml/qml/qqmlopenmetaobject_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLOPENMETAOBJECT_H +#define QQMLOPENMETAOBJECT_H + +#include <QtCore/QMetaObject> +#include <QtCore/QObject> + +#include <private/qqmlrefcount_p.h> +#include <private/qqmlcleanup_p.h> +#include <private/qtqmlglobal_p.h> +#include <private/qobject_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlEngine; +class QMetaPropertyBuilder; +class QQmlOpenMetaObjectTypePrivate; +class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObjectType : public QQmlRefCount, public QQmlCleanup +{ +public: + QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine); + ~QQmlOpenMetaObjectType(); + + int createProperty(const QByteArray &name); + + int propertyOffset() const; + int signalOffset() const; + +protected: + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual void clear(); + +private: + QQmlOpenMetaObjectTypePrivate *d; + friend class QQmlOpenMetaObject; + friend class QQmlOpenMetaObjectPrivate; +}; + +class QQmlOpenMetaObjectPrivate; +class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QQmlOpenMetaObject(QObject *, bool = true); + QQmlOpenMetaObject(QObject *, QQmlOpenMetaObjectType *, bool = true); + ~QQmlOpenMetaObject(); + + QVariant value(const QByteArray &) const; + bool setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); + QVariant &operator[](int); + bool hasValue(int) const; + + int count() const; + QByteArray name(int) const; + + QObject *object() const; + virtual QVariant initialValue(int); + + // Be careful - once setCached(true) is called createProperty() is no + // longer automatically called for new properties. + void setCached(bool); + + QQmlOpenMetaObjectType *type() const; + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int createProperty(const char *, const char *); + + virtual void propertyRead(int); + virtual void propertyWrite(int); + virtual void propertyWritten(int); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + + QAbstractDynamicMetaObject *parent() const; + +private: + QQmlOpenMetaObjectPrivate *d; + friend class QQmlOpenMetaObjectType; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLOPENMETAOBJECT_H diff --git a/src/qml/qml/qqmlparserstatus.cpp b/src/qml/qml/qqmlparserstatus.cpp new file mode 100644 index 0000000000..d4e415a069 --- /dev/null +++ b/src/qml/qml/qqmlparserstatus.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlparserstatus.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlParserStatus + \since 4.7 + \brief The QQmlParserStatus class provides updates on the QML parser state. + + QQmlParserStatus provides a mechanism for classes instantiated by + a QQmlEngine to receive notification at key points in their creation. + + This class is often used for optimization purposes, as it allows you to defer an + expensive operation until after all the properties have been set on an + object. For example, QML's \l {Text} element uses the parser status + to defer text layout until all of its properties have been set (we + don't want to layout when the \c text is assigned, and then relayout + when the \c font is assigned, and relayout again when the \c width is assigned, + and so on). + + To use QQmlParserStatus, you must inherit both a QObject-derived class + and QQmlParserStatus, and use the Q_INTERFACES() macro. + + \code + class MyObject : public QObject, public QQmlParserStatus + { + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + public: + MyObject(QObject *parent = 0); + ... + void classBegin(); + void componentComplete(); + } + \endcode +*/ + +/*! \internal */ +QQmlParserStatus::QQmlParserStatus() +: d(0) +{ +} + +/*! \internal */ +QQmlParserStatus::~QQmlParserStatus() +{ + if(d) + (*d) = 0; +} + +/*! + \fn void QQmlParserStatus::classBegin() + + Invoked after class creation, but before any properties have been set. +*/ + +/*! + \fn void QQmlParserStatus::componentComplete() + + Invoked after the root component that caused this instantiation has + completed construction. At this point all static values and binding values + have been assigned to the class. +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlparserstatus.h b/src/qml/qml/qqmlparserstatus.h new file mode 100644 index 0000000000..9f06f45b06 --- /dev/null +++ b/src/qml/qml/qqmlparserstatus.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPARSERSTATUS_H +#define QQMLPARSERSTATUS_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class Q_QML_EXPORT QQmlParserStatus +{ +public: + QQmlParserStatus(); + virtual ~QQmlParserStatus(); + + virtual void classBegin()=0; + virtual void componentComplete()=0; + +private: + friend class QQmlVME; + friend class QQmlComponent; + friend class QQmlComponentPrivate; + friend class QQmlEnginePrivate; + QQmlParserStatus **d; +}; +Q_DECLARE_INTERFACE(QQmlParserStatus, "com.trolltech.qml.QQmlParserStatus") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLPARSERSTATUS_H diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h new file mode 100644 index 0000000000..b4c6fc3a12 --- /dev/null +++ b/src/qml/qml/qqmlprivate.h @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPRIVATE_H +#define QQMLPRIVATE_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/qglobal.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +typedef QObject *(*QQmlAttachedPropertiesFunc)(QObject *); + +template <typename TYPE> +class QQmlTypeInfo +{ +public: + enum { + hasAttachedProperties = 0 + }; +}; + + +class QJSValue; +class QJSEngine; +class QQmlEngine; +class QQmlCustomParser; +namespace QQmlPrivate +{ + void Q_QML_EXPORT qdeclarativeelement_destructor(QObject *); + template<typename T> + class QQmlElement : public T + { + public: + virtual ~QQmlElement() { + QQmlPrivate::qdeclarativeelement_destructor(this); + } + }; + + template<typename T> + void createInto(void *memory) { new (memory) QQmlElement<T>; } + + template<typename T> + QObject *createParent(QObject *p) { return new T(p); } + + template<class From, class To, int N> + struct StaticCastSelectorClass + { + static inline int cast() { return -1; } + }; + + template<class From, class To> + struct StaticCastSelectorClass<From, To, sizeof(int)> + { + static inline int cast() { return int(reinterpret_cast<quintptr>(static_cast<To *>(reinterpret_cast<From *>(0x10000000)))) - 0x10000000; } + }; + + template<class From, class To> + struct StaticCastSelector + { + typedef int yes_type; + typedef char no_type; + + static yes_type check(To *); + static no_type check(...); + + static inline int cast() + { + return StaticCastSelectorClass<From, To, sizeof(check(reinterpret_cast<From *>(0)))>::cast(); + } + }; + + template <typename T> + struct has_attachedPropertiesMember + { + static bool const value = QQmlTypeInfo<T>::hasAttachedProperties; + }; + + template <typename T, bool hasMember> + class has_attachedPropertiesMethod + { + public: + typedef int yes_type; + typedef char no_type; + + template<typename ReturnType> + static yes_type check(ReturnType *(*)(QObject *)); + static no_type check(...); + + static bool const value = sizeof(check(&T::qmlAttachedProperties)) == sizeof(yes_type); + }; + + template <typename T> + class has_attachedPropertiesMethod<T, false> + { + public: + static bool const value = false; + }; + + template<typename T, int N> + class AttachedPropertySelector + { + public: + static inline QQmlAttachedPropertiesFunc func() { return 0; } + static inline const QMetaObject *metaObject() { return 0; } + }; + template<typename T> + class AttachedPropertySelector<T, 1> + { + static inline QObject *attachedProperties(QObject *obj) { + return T::qmlAttachedProperties(obj); + } + template<typename ReturnType> + static inline const QMetaObject *attachedPropertiesMetaObject(ReturnType *(*)(QObject *)) { + return &ReturnType::staticMetaObject; + } + public: + static inline QQmlAttachedPropertiesFunc func() { + return &attachedProperties; + } + static inline const QMetaObject *metaObject() { + return attachedPropertiesMetaObject(&T::qmlAttachedProperties); + } + }; + + template<typename T> + inline QQmlAttachedPropertiesFunc attachedPropertiesFunc() + { + return AttachedPropertySelector<T, has_attachedPropertiesMethod<T, has_attachedPropertiesMember<T>::value>::value>::func(); + } + + template<typename T> + inline const QMetaObject *attachedPropertiesMetaObject() + { + return AttachedPropertySelector<T, has_attachedPropertiesMethod<T, has_attachedPropertiesMember<T>::value>::value>::metaObject(); + } + + enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent }; + typedef AutoParentResult (*AutoParentFunction)(QObject *object, QObject *parent); + + struct RegisterType { + int version; + + int typeId; + int listId; + int objectSize; + void (*create)(void *); + QString noCreationReason; + + const char *uri; + int versionMajor; + int versionMinor; + const char *elementName; + const QMetaObject *metaObject; + + QQmlAttachedPropertiesFunc attachedPropertiesFunction; + const QMetaObject *attachedPropertiesMetaObject; + + int parserStatusCast; + int valueSourceCast; + int valueInterceptorCast; + + QObject *(*extensionObjectCreate)(QObject *); + const QMetaObject *extensionMetaObject; + + QQmlCustomParser *customParser; + int revision; + // If this is extended ensure "version" is bumped!!! + }; + + struct RegisterInterface { + int version; + + int typeId; + int listId; + + const char *iid; + }; + + struct RegisterAutoParent { + int version; + + AutoParentFunction function; + }; + + struct RegisterModuleApi { + int version; + + const char *uri; + int versionMajor; + int versionMinor; + + QJSValue (*scriptApi)(QQmlEngine *, QJSEngine *); + QObject *(*qobjectApi)(QQmlEngine *, QJSEngine *); + }; + + enum RegistrationType { + TypeRegistration = 0, + InterfaceRegistration = 1, + AutoParentRegistration = 2, + ModuleApiRegistration = 3 + }; + + int Q_QML_EXPORT qmlregister(RegistrationType, void *); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLPRIVATE_H diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp new file mode 100644 index 0000000000..6321592e9a --- /dev/null +++ b/src/qml/qml/qqmlproperty.cpp @@ -0,0 +1,1917 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlproperty.h" +#include "qqmlproperty_p.h" + +#include "qqml.h" +#include "qqmlbinding_p.h" +#include "qqmlcontext.h" +#include "qqmlcontext_p.h" +#include "qqmlboundsignal_p.h" +#include "qqmlengine.h" +#include "qqmlengine_p.h" +#include "qqmldata_p.h" +#include "qqmlstringconverters_p.h" +#include "qqmllist_p.h" +#include "qqmlcompiler_p.h" +#include "qqmlvmemetaobject_p.h" +#include "qqmlexpression_p.h" + +#include <QStringList> +#include <QtCore/qdebug.h> + +#include <math.h> + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<qreal>) +Q_DECLARE_METATYPE(QList<bool>) +Q_DECLARE_METATYPE(QList<QString>) +Q_DECLARE_METATYPE(QList<QUrl>) + +QT_BEGIN_NAMESPACE + +/*! +\class QQmlProperty +\since 4.7 +\brief The QQmlProperty class abstracts accessing properties on objects created from QML. + +As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect +and interact with objects created by QML. However, some of the new features provided by QML - such +as type safety and attached properties - are most easily used through the QQmlProperty class +that simplifies some of their natural complexity. + +Unlike QMetaProperty which represents a property on a class type, QQmlProperty encapsulates +a property on a specific object instance. To read a property's value, programmers create a +QQmlProperty instance and call the read() method. Likewise to write a property value the +write() method is used. + +For example, for the following QML code: + +\qml +// MyItem.qml +import QtQuick 2.0 + +Text { text: "A bit of text" } +\endqml + +The \l Text object's properties could be accessed using QQmlProperty, like this: + +\code +#include <QQmlProperty> +#include <QGraphicsObject> + +... + +QQuickView view(QUrl::fromLocalFile("MyItem.qml")); +QQmlProperty property(view.rootObject(), "font.pixelSize"); +qWarning() << "Current pixel size:" << property.read().toInt(); +property.write(24); +qWarning() << "Pixel size should now be 24:" << property.read().toInt(); +\endcode +*/ + +/*! + Create an invalid QQmlProperty. +*/ +QQmlProperty::QQmlProperty() +: d(0) +{ +} + +/*! \internal */ +QQmlProperty::~QQmlProperty() +{ + if (d) + d->release(); + d = 0; +} + +/*! + Creates a QQmlProperty for the default property of \a obj. If there is no + default property, an invalid QQmlProperty will be created. + */ +QQmlProperty::QQmlProperty(QObject *obj) +: d(new QQmlPropertyPrivate) +{ + d->initDefault(obj); +} + +/*! + Creates a QQmlProperty for the default property of \a obj + using the \l{QQmlContext} {context} \a ctxt. If there is + no default property, an invalid QQmlProperty will be + created. + */ +QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt) +: d(new QQmlPropertyPrivate) +{ + d->context = ctxt?QQmlContextData::get(ctxt):0; + d->engine = ctxt?ctxt->engine():0; + d->initDefault(obj); +} + +/*! + Creates a QQmlProperty for the default property of \a obj + using the environment for instantiating QML components that is + provided by \a engine. If there is no default property, an + invalid QQmlProperty will be created. + */ +QQmlProperty::QQmlProperty(QObject *obj, QQmlEngine *engine) + : d(new QQmlPropertyPrivate) +{ + d->context = 0; + d->engine = engine; + d->initDefault(obj); +} + +/*! + Initialize from the default property of \a obj +*/ +void QQmlPropertyPrivate::initDefault(QObject *obj) +{ + if (!obj) + return; + + QMetaProperty p = QQmlMetaType::defaultProperty(obj); + core.load(p); + if (core.isValid()) + object = obj; +} + +/*! + Creates a QQmlProperty for the property \a name of \a obj. + */ +QQmlProperty::QQmlProperty(QObject *obj, const QString &name) +: d(new QQmlPropertyPrivate) +{ + d->initProperty(obj, name); + if (!isValid()) d->object = 0; +} + +/*! + Creates a QQmlProperty for the property \a name of \a obj + using the \l{QQmlContext} {context} \a ctxt. + + Creating a QQmlProperty without a context will render some + properties - like attached properties - inaccessible. +*/ +QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt) +: d(new QQmlPropertyPrivate) +{ + d->context = ctxt?QQmlContextData::get(ctxt):0; + d->engine = ctxt?ctxt->engine():0; + d->initProperty(obj, name); + if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } +} + +/*! + Creates a QQmlProperty for the property \a name of \a obj + using the environment for instantiating QML components that is + provided by \a engine. + */ +QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine) +: d(new QQmlPropertyPrivate) +{ + d->context = 0; + d->engine = engine; + d->initProperty(obj, name); + if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } +} + +Q_GLOBAL_STATIC(QQmlValueTypeFactory, qmlValueTypes); + +QQmlPropertyPrivate::QQmlPropertyPrivate() +: context(0), engine(0), object(0), isNameCached(false) +{ +} + +QQmlContextData *QQmlPropertyPrivate::effectiveContext() const +{ + if (context) return context; + else if (engine) return QQmlContextData::get(engine->rootContext()); + else return 0; +} + +void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) +{ + if (!obj) return; + + QQmlTypeNameCache *typeNameCache = context?context->imports:0; + + QStringList path = name.split(QLatin1Char('.')); + if (path.isEmpty()) return; + + QObject *currentObject = obj; + + // Everything up to the last property must be an "object type" property + for (int ii = 0; ii < path.count() - 1; ++ii) { + const QString &pathName = path.at(ii); + + if (typeNameCache) { + QQmlTypeNameCache::Result r = typeNameCache->query(pathName); + if (r.isValid()) { + if (r.type) { + QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + } else if (r.importNamespace) { + if ((ii + 1) == path.count()) return; // No type following the namespace + + ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); + if (!r.type) return; // Invalid type in namespace + + QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + + } else if (r.scriptIndex != -1) { + return; // Not a type + } else { + Q_ASSERT(!"Unreachable"); + } + continue; + } + + } + + QQmlPropertyData local; + QQmlPropertyData *property = + QQmlPropertyCache::property(engine, obj, pathName, local); + + if (!property) return; // Not a property + if (property->isFunction()) + return; // Not an object property + + if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) { + // We're now at a value type property. We can use a global valuetypes array as we + // never actually use the objects, just look up their properties. + QObject *typeObject = (*qmlValueTypes())[property->propType]; + if (!typeObject) return; // Not a value type + + int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); + if (idx == -1) return; // Value type property does not exist + + QMetaProperty vtProp = typeObject->metaObject()->property(idx); + + typedef QQmlPropertyData PCD; + + Q_ASSERT(PCD::flagsForProperty(vtProp) <= PCD::ValueTypeFlagMask); + Q_ASSERT(vtProp.userType() <= 0xFF); + Q_ASSERT(idx <= 0xFF); + + object = currentObject; + core = *property; + core.setFlags(core.getFlags() | PCD::IsValueTypeVirtual); + core.valueTypeFlags = PCD::flagsForProperty(vtProp); + core.valueTypePropType = vtProp.userType(); + core.valueTypeCoreIndex = idx; + + return; + } else { + if (!property->isQObject()) + return; // Not an object property + + void *args[] = { ¤tObject, 0 }; + QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args); + if (!currentObject) return; // No value + + } + + } + + const QString &terminal = path.last(); + + if (terminal.count() >= 3 && + terminal.at(0) == QLatin1Char('o') && + terminal.at(1) == QLatin1Char('n') && + terminal.at(2).isUpper()) { + + QString signalName = terminal.mid(2); + signalName[0] = signalName.at(0).toLower(); + + QMetaMethod method = findSignalByName(currentObject->metaObject(), signalName.toLatin1().constData()); + if (method.signature()) { + object = currentObject; + core.load(method); + return; + } + } + + // Property + QQmlPropertyData local; + QQmlPropertyData *property = + QQmlPropertyCache::property(engine, currentObject, terminal, local); + if (property && !property->isFunction()) { + object = currentObject; + core = *property; + nameCache = terminal; + isNameCached = true; + } +} + +/*! + Create a copy of \a other. +*/ +QQmlProperty::QQmlProperty(const QQmlProperty &other) +{ + d = other.d; + if (d) + d->addref(); +} + +/*! + \enum QQmlProperty::PropertyTypeCategory + + This enum specifies a category of QML property. + + \value InvalidCategory The property is invalid, or is a signal property. + \value List The property is a QQmlListProperty list property + \value Object The property is a QObject derived type pointer + \value Normal The property is a normal value property. + */ + +/*! + \enum QQmlProperty::Type + + This enum specifies a type of QML property. + + \value Invalid The property is invalid. + \value Property The property is a regular Qt property. + \value SignalProperty The property is a signal property. +*/ + +/*! + Returns the property category. +*/ +QQmlProperty::PropertyTypeCategory QQmlProperty::propertyTypeCategory() const +{ + return d ? d->propertyTypeCategory() : InvalidCategory; +} + +QQmlProperty::PropertyTypeCategory +QQmlPropertyPrivate::propertyTypeCategory() const +{ + uint type = this->type(); + + if (isValueType()) { + return QQmlProperty::Normal; + } else if (type & QQmlProperty::Property) { + int type = propertyType(); + if (type == QVariant::Invalid) + return QQmlProperty::InvalidCategory; + else if (QQmlValueTypeFactory::isValueType((uint)type)) + return QQmlProperty::Normal; + else if (core.isQObject()) + return QQmlProperty::Object; + else if (core.isQList()) + return QQmlProperty::List; + else + return QQmlProperty::Normal; + } else { + return QQmlProperty::InvalidCategory; + } +} + +/*! + Returns the type name of the property, or 0 if the property has no type + name. +*/ +const char *QQmlProperty::propertyTypeName() const +{ + if (!d) + return 0; + if (d->isValueType()) { + + QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0; + QQmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QQmlValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + const char *rv = valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName(); + + if (!ep) delete valueType; + + return rv; + } else if (d->object && type() & Property && d->core.isValid()) { + return d->object->metaObject()->property(d->core.coreIndex).typeName(); + } else { + return 0; + } +} + +/*! + Returns true if \a other and this QQmlProperty represent the same + property. +*/ +bool QQmlProperty::operator==(const QQmlProperty &other) const +{ + if (!d || !other.d) + return false; + // category is intentially omitted here as it is generated + // from the other members + return d->object == other.d->object && + d->core.coreIndex == other.d->core.coreIndex && + d->core.isValueTypeVirtual() == other.d->core.isValueTypeVirtual() && + (!d->core.isValueTypeVirtual() || + (d->core.valueTypeCoreIndex == other.d->core.valueTypeCoreIndex && + d->core.valueTypePropType == other.d->core.valueTypePropType)); +} + +/*! + Returns the QVariant type of the property, or QVariant::Invalid if the + property has no QVariant type. +*/ +int QQmlProperty::propertyType() const +{ + return d ? d->propertyType() : int(QVariant::Invalid); +} + +bool QQmlPropertyPrivate::isValueType() const +{ + return core.isValueTypeVirtual(); +} + +int QQmlPropertyPrivate::propertyType() const +{ + uint type = this->type(); + if (isValueType()) { + return core.valueTypePropType; + } else if (type & QQmlProperty::Property) { + return core.propType; + } else { + return QVariant::Invalid; + } +} + +QQmlProperty::Type QQmlPropertyPrivate::type() const +{ + if (core.isFunction()) + return QQmlProperty::SignalProperty; + else if (core.isValid()) + return QQmlProperty::Property; + else + return QQmlProperty::Invalid; +} + +/*! + Returns the type of the property. +*/ +QQmlProperty::Type QQmlProperty::type() const +{ + return d ? d->type() : Invalid; +} + +/*! + Returns true if this QQmlProperty represents a regular Qt property. +*/ +bool QQmlProperty::isProperty() const +{ + return type() & Property; +} + +/*! + Returns true if this QQmlProperty represents a QML signal property. +*/ +bool QQmlProperty::isSignalProperty() const +{ + return type() & SignalProperty; +} + +/*! + Returns the QQmlProperty's QObject. +*/ +QObject *QQmlProperty::object() const +{ + return d ? d->object : 0; +} + +/*! + Assign \a other to this QQmlProperty. +*/ +QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other) +{ + if (d) + d->release(); + d = other.d; + if (d) + d->addref(); + + return *this; +} + +/*! + Returns true if the property is writable, otherwise false. +*/ +bool QQmlProperty::isWritable() const +{ + if (!d) + return false; + if (!d->object) + return false; + if (d->core.isQList()) //list + return true; + else if (d->core.isFunction()) //signal handler + return false; + else if (d->core.isValid()) //normal property + return d->core.isWritable(); + else + return false; +} + +/*! + Returns true if the property is designable, otherwise false. +*/ +bool QQmlProperty::isDesignable() const +{ + if (!d) + return false; + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); + else + return false; +} + +/*! + Returns true if the property is resettable, otherwise false. +*/ +bool QQmlProperty::isResettable() const +{ + if (!d) + return false; + if (type() & Property && d->core.isValid() && d->object) + return d->core.isResettable(); + else + return false; +} + +/*! + Returns true if the QQmlProperty refers to a valid property, otherwise + false. +*/ +bool QQmlProperty::isValid() const +{ + if (!d) + return false; + return type() != Invalid; +} + +/*! + Return the name of this QML property. +*/ +QString QQmlProperty::name() const +{ + if (!d) + return QString(); + if (!d->isNameCached) { + // ### + if (!d->object) { + } else if (d->isValueType()) { + QString rv = d->core.name(d->object) + QLatin1Char('.'); + + QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0; + QQmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QQmlValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + const char *vtName = valueType->metaObject()->property(d->core.valueTypeCoreIndex).name(); + rv += QString::fromUtf8(vtName); + + if (!ep) delete valueType; + + d->nameCache = rv; + } else if (type() & SignalProperty) { + QString name = QLatin1String("on") + d->core.name(d->object); + name[2] = name.at(2).toUpper(); + d->nameCache = name; + } else { + d->nameCache = d->core.name(d->object); + } + d->isNameCached = true; + } + + return d->nameCache; +} + +/*! + Returns the \l{QMetaProperty} {Qt property} associated with + this QML property. + */ +QMetaProperty QQmlProperty::property() const +{ + if (!d) + return QMetaProperty(); + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex); + else + return QMetaProperty(); +} + +/*! + Return the QMetaMethod for this property if it is a SignalProperty, + otherwise returns an invalid QMetaMethod. +*/ +QMetaMethod QQmlProperty::method() const +{ + if (!d) + return QMetaMethod(); + if (type() & SignalProperty && d->object) + return d->object->metaObject()->method(d->core.coreIndex); + else + return QMetaMethod(); +} + +/*! + Returns the binding associated with this property, or 0 if no binding + exists. +*/ +QQmlAbstractBinding * +QQmlPropertyPrivate::binding(const QQmlProperty &that) +{ + if (!that.d || !that.isProperty() || !that.d->object) + return 0; + + return binding(that.d->object, that.d->core.coreIndex, + that.d->core.getValueTypeCoreIndex()); +} + +/*! + Set the binding associated with this property to \a newBinding. Returns + the existing binding (if any), otherwise 0. + + \a newBinding will be enabled, and the returned binding (if any) will be + disabled. + + Ownership of \a newBinding transfers to QML. Ownership of the return value + is assumed by the caller. + + \a flags is passed through to the binding and is used for the initial update (when + the binding sets the initial value, it will use these flags for the write). +*/ +QQmlAbstractBinding * +QQmlPropertyPrivate::setBinding(const QQmlProperty &that, + QQmlAbstractBinding *newBinding, + WriteFlags flags) +{ + if (!that.d || !that.isProperty() || !that.d->object) { + if (newBinding) + newBinding->destroy(); + return 0; + } + + if (newBinding) { + // In the case that the new binding is provided, we must target the property it + // is associated with. If we don't do this, retargetBinding() can fail. + QObject *object = newBinding->object(); + int pi = newBinding->propertyIndex(); + + int core = pi & 0xFFFFFF; + int vt = (pi & 0xFF000000)?(pi >> 24):-1; + + return setBinding(object, core, vt, newBinding, flags); + } else { + return setBinding(that.d->object, that.d->core.coreIndex, + that.d->core.getValueTypeCoreIndex(), + newBinding, flags); + } +} + +QQmlAbstractBinding * +QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) +{ + QQmlData *data = QQmlData::get(object); + if (!data) + return 0; + + QQmlPropertyData *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->isAlias()) { + const QQmlVMEMetaObject *vme = + static_cast<const QQmlVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1) + return 0; + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; + return binding(aObject, aCoreIndex, aValueTypeIndex); + } + + if (!data->hasBindingBit(coreIndex)) + return 0; + + QQmlAbstractBinding *binding = data->bindings; + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + + if (binding && valueTypeIndex != -1) { + if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) { + int index = coreIndex | (valueTypeIndex << 24); + binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); + } + } + + return binding; +} + +void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, + QObject **targetObject, int *targetBindingIndex) +{ + int coreIndex = bindingIndex & 0xFFFFFF; + int valueTypeIndex = bindingIndex >> 24; + if (valueTypeIndex == 0) valueTypeIndex = -1; + + QQmlData *data = QQmlData::get(object, false); + if (data) { + QQmlPropertyData *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->isAlias()) { + const QQmlVMEMetaObject *vme = + static_cast<const QQmlVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + + int aBindingIndex = aCoreIndex; + if (aValueTypeIndex != -1) + aBindingIndex |= aValueTypeIndex << 24; + else if (valueTypeIndex != -1) + aBindingIndex |= valueTypeIndex << 24; + + findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); + return; + } + } + } + + *targetObject = object; + *targetBindingIndex = bindingIndex; +} + +QQmlAbstractBinding * +QQmlPropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, + QQmlAbstractBinding *newBinding, WriteFlags flags) +{ + QQmlData *data = QQmlData::get(object, 0 != newBinding); + QQmlAbstractBinding *binding = 0; + + if (data) { + QQmlPropertyData *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->isAlias()) { + const QQmlVMEMetaObject *vme = + static_cast<const QQmlVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; + return setBinding(aObject, aCoreIndex, aValueTypeIndex, newBinding, flags); + } + } + + if (data && data->hasBindingBit(coreIndex)) { + binding = data->bindings; + + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + } + + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) + binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); + + if (binding) { + binding->removeFromObject(); + binding->setEnabled(false, 0); + } + + if (newBinding) { + if (newBinding->propertyIndex() != index || newBinding->object() != object) + newBinding->retargetBinding(object, index); + + Q_ASSERT(newBinding->propertyIndex() == index); + Q_ASSERT(newBinding->object() == object); + + newBinding->addToObject(); + newBinding->setEnabled(true, flags); + } + + return binding; +} + +QQmlAbstractBinding * +QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, + QQmlAbstractBinding *newBinding) +{ + QQmlData *data = QQmlData::get(object, 0 != newBinding); + QQmlAbstractBinding *binding = 0; + + if (data) { + QQmlPropertyData *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->isAlias()) { + const QQmlVMEMetaObject *vme = + static_cast<const QQmlVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; + return setBindingNoEnable(aObject, aCoreIndex, aValueTypeIndex, newBinding); + } + } + + if (data && data->hasBindingBit(coreIndex)) { + binding = data->bindings; + + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + } + + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) + binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); + + if (binding) + binding->removeFromObject(); + + if (newBinding) { + if (newBinding->propertyIndex() != index || newBinding->object() != object) + newBinding->retargetBinding(object, index); + + Q_ASSERT(newBinding->propertyIndex() == index); + Q_ASSERT(newBinding->object() == object); + + newBinding->addToObject(); + } + + return binding; +} + +/*! + Returns the expression associated with this signal property, or 0 if no + signal expression exists. +*/ +QQmlExpression * +QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) +{ + if (!(that.type() & QQmlProperty::SignalProperty)) + return 0; + + const QObjectList &children = that.d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QQmlBoundSignal *signal = QQmlBoundSignal::cast(child); + if (signal && signal->index() == that.index()) + return signal->expression(); + } + + return 0; +} + +/*! + Set the signal expression associated with this signal property to \a expr. + Returns the existing signal expression (if any), otherwise 0. + + Ownership of \a expr transfers to QML. Ownership of the return value is + assumed by the caller. +*/ +QQmlExpression * +QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, + QQmlExpression *expr) +{ + if (!(that.type() & QQmlProperty::SignalProperty)) { + delete expr; + return 0; + } + + const QObjectList &children = that.d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QQmlBoundSignal *signal = QQmlBoundSignal::cast(child); + if (signal && signal->index() == that.index()) + return signal->setExpression(expr); + } + + if (expr) { + QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, that.method(), that.d->object); + return signal->setExpression(expr); + } else { + return 0; + } +} + +/*! + Returns the property value. +*/ +QVariant QQmlProperty::read() const +{ + if (!d) + return QVariant(); + if (!d->object) + return QVariant(); + + if (type() & SignalProperty) { + + return QVariant(); + + } else if (type() & Property) { + + return d->readValueProperty(); + + } + return QVariant(); +} + +/*! +Return the \a name property value of \a object. This method is equivalent to: +\code + QQmlProperty p(object, name); + p.read(); +\endcode +*/ +QVariant QQmlProperty::read(QObject *object, const QString &name) +{ + QQmlProperty p(object, name); + return p.read(); +} + +/*! + Return the \a name property value of \a object using the + \l{QQmlContext} {context} \a ctxt. This method is + equivalent to: + + \code + QQmlProperty p(object, name, context); + p.read(); + \endcode +*/ +QVariant QQmlProperty::read(QObject *object, const QString &name, QQmlContext *ctxt) +{ + QQmlProperty p(object, name, ctxt); + return p.read(); +} + +/*! + + Return the \a name property value of \a object using the environment + for instantiating QML components that is provided by \a engine. . + This method is equivalent to: + + \code + QQmlProperty p(object, name, engine); + p.read(); + \endcode +*/ +QVariant QQmlProperty::read(QObject *object, const QString &name, QQmlEngine *engine) +{ + QQmlProperty p(object, name, engine); + return p.read(); +} + +QVariant QQmlPropertyPrivate::readValueProperty() +{ + if (isValueType()) { + + QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; + QQmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[core.propType]; + else valueType = QQmlValueTypeFactory::valueType(core.propType); + Q_ASSERT(valueType); + + valueType->read(object, core.coreIndex); + + QVariant rv = valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType); + + if (!ep) delete valueType; + return rv; + + } else if (core.isQList()) { + + QQmlListProperty<QObject> prop; + void *args[] = { &prop, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine)); + + } else if (core.isQObject()) { + + QObject *rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + return QVariant::fromValue(rv); + + } else { + + return object->metaObject()->property(core.coreIndex).read(object.data()); + + } +} + +static QUrl urlFromUserString(const QByteArray &data) +{ + QUrl u; + if (!data.isEmpty()) + { + // Preserve any valid percent-encoded octets supplied by the source + u.setEncodedUrl(data, QUrl::TolerantMode); + } + return u; +} + +static QUrl urlFromUserString(const QString &data) +{ + return urlFromUserString(data.toUtf8()); +} + +// helper function to allow assignment / binding to QList<QUrl> properties. +static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context) +{ + QList<QUrl> urls; + if (value.userType() == qMetaTypeId<QUrl>()) { + urls.append(value.toUrl()); + } else if (value.userType() == qMetaTypeId<QString>()) { + urls.append(urlFromUserString(value.toString())); + } else if (value.userType() == qMetaTypeId<QByteArray>()) { + urls.append(urlFromUserString(value.toByteArray())); + } else if (value.userType() == qMetaTypeId<QList<QUrl> >()) { + urls = value.value<QList<QUrl> >(); + } else if (value.userType() == qMetaTypeId<QStringList>()) { + QStringList urlStrings = value.value<QStringList>(); + for (int i = 0; i < urlStrings.size(); ++i) + urls.append(urlFromUserString(urlStrings.at(i))); + } else if (value.userType() == qMetaTypeId<QList<QString> >()) { + QList<QString> urlStrings = value.value<QList<QString> >(); + for (int i = 0; i < urlStrings.size(); ++i) + urls.append(urlFromUserString(urlStrings.at(i))); + } // note: QList<QByteArray> is not currently supported. + + QList<QUrl> resolvedUrls; + for (int i = 0; i < urls.size(); ++i) { + QUrl u = urls.at(i); + if (context && u.isRelative() && !u.isEmpty()) + u = context->resolvedUrl(u); + resolvedUrls.append(u); + } + + return QVariant::fromValue<QList<QUrl> >(resolvedUrls); +} + +//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC! +bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags) +{ + if (!object || !prop.isWritable()) + return false; + + QVariant v = value; + if (prop.isEnumType()) { + QMetaEnum menum = prop.enumerator(); + if (v.userType() == QVariant::String +#ifdef QT3_SUPPORT + || v.userType() == QVariant::CString +#endif + ) { + bool ok; + if (prop.isFlagType()) + v = QVariant(menum.keysToValue(value.toByteArray(), &ok)); + else + v = QVariant(menum.keyToValue(value.toByteArray(), &ok)); + if (!ok) + return false; + } else if (v.userType() != QVariant::Int && v.userType() != QVariant::UInt) { + int enumMetaTypeId = QMetaType::type(QByteArray(menum.scope() + QByteArray("::") + menum.name())); + if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + return false; + v = QVariant(*reinterpret_cast<const int *>(v.constData())); + } + v.convert(QVariant::Int); + } + + // the status variable is changed by qt_metacall to indicate what it did + // this feature is currently only used by QtDBus and should not be depended + // upon. Don't change it without looking into QDBusAbstractInterface first + // -1 (unchanged): normal qt_metacall, result stored in argv[0] + // changed: result stored directly in value, return the value of status + int status = -1; + void *argv[] = { v.data(), &v, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv); + return status; +} + +bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) +{ + return writeValueProperty(object, engine, core, value, effectiveContext(), flags); +} + +bool +QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlEngine *engine, + const QQmlPropertyData &core, + const QVariant &value, + QQmlContextData *context, WriteFlags flags) +{ + // Remove any existing bindings on this property + if (!(flags & DontRemoveBinding) && object) { + QQmlAbstractBinding *binding = setBinding(object, core.coreIndex, + core.getValueTypeCoreIndex(), + 0, flags); + if (binding) binding->destroy(); + } + + bool rv = false; + if (core.isValueTypeVirtual()) { + QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; + + QQmlValueType *writeBack = 0; + if (ep) { + writeBack = ep->valueTypes[core.propType]; + } else { + writeBack = QQmlValueTypeFactory::valueType(core.propType); + } + + writeBack->read(object, core.coreIndex); + + QQmlPropertyData data = core; + data.setFlags(QQmlPropertyData::Flag(core.valueTypeFlags)); + data.coreIndex = core.valueTypeCoreIndex; + data.propType = core.valueTypePropType; + + rv = write(writeBack, data, value, context, flags); + + writeBack->write(object, core.coreIndex, flags); + if (!ep) delete writeBack; + + } else { + + rv = write(object, core, value, context, flags); + + } + + return rv; +} + +bool QQmlPropertyPrivate::write(QObject *object, + const QQmlPropertyData &property, + const QVariant &value, QQmlContextData *context, + WriteFlags flags) +{ + int coreIdx = property.coreIndex; + int status = -1; //for dbus + + if (property.isEnum()) { + QMetaProperty prop = object->metaObject()->property(property.coreIndex); + QVariant v = value; + // Enum values come through the script engine as doubles + if (value.userType() == QVariant::Double) { + double integral; + double fractional = modf(value.toDouble(), &integral); + if (qFuzzyIsNull(fractional)) + v.convert(QVariant::Int); + } + return writeEnumProperty(prop, coreIdx, object, v, flags); + } + + int propertyType = property.propType; + int variantType = value.userType(); + + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); + + if (propertyType == QVariant::Url) { + + QUrl u; + bool found = false; + if (variantType == QVariant::Url) { + u = value.toUrl(); + found = true; + } else if (variantType == QVariant::ByteArray) { + u = urlFromUserString(value.toByteArray()); + found = true; + } else if (variantType == QVariant::String) { + u = urlFromUserString(value.toString()); + found = true; + } + + if (!found) + return false; + + if (context && u.isRelative() && !u.isEmpty()) + u = context->resolvedUrl(u); + int status = -1; + void *argv[] = { &u, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); + + } else if (propertyType == qMetaTypeId<QList<QUrl> >()) { + QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl> >(); + int status = -1; + void *argv[] = { &urlSeq, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); + } else if (variantType == propertyType) { + + void *a[] = { (void *)value.constData(), 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + + } else if (qMetaTypeId<QVariant>() == propertyType) { + + void *a[] = { (void *)&value, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + + } else if (property.isQObject()) { + + const QMetaObject *valMo = rawMetaObjectForType(enginePriv, value.userType()); + + if (!valMo) + return false; + + QObject *o = *(QObject **)value.constData(); + const QMetaObject *propMo = rawMetaObjectForType(enginePriv, propertyType); + + if (o) valMo = o->metaObject(); + + if (canConvert(valMo, propMo)) { + void *args[] = { &o, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, + args); + } else if (!o && canConvert(propMo, valMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + void *args[] = { &o, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, + args); + } else { + return false; + } + + } else if (property.isQList()) { + + const QMetaObject *listType = 0; + if (enginePriv) { + listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); + } else { + QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType)); + if (!type) return false; + listType = type->baseMetaObject(); + } + if (!listType) return false; + + QQmlListProperty<void> prop; + void *args[] = { &prop, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args); + + if (!prop.clear) return false; + + prop.clear(&prop); + + if (value.userType() == qMetaTypeId<QQmlListReference>()) { + QQmlListReference qdlr = value.value<QQmlListReference>(); + + for (int ii = 0; ii < qdlr.count(); ++ii) { + QObject *o = qdlr.at(ii); + if (o && !canConvert(o->metaObject(), listType)) + o = 0; + prop.append(&prop, (void *)o); + } + } else if (value.userType() == qMetaTypeId<QList<QObject *> >()) { + const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value); + + for (int ii = 0; ii < list.count(); ++ii) { + QObject *o = list.at(ii); + if (o && !canConvert(o->metaObject(), listType)) + o = 0; + prop.append(&prop, (void *)o); + } + } else { + QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value); + if (o && !canConvert(o->metaObject(), listType)) + o = 0; + prop.append(&prop, (void *)o); + } + + } else { + Q_ASSERT(variantType != propertyType); + + bool ok = false; + QVariant v; + if (variantType == QVariant::String) + v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok); + if (!ok) { + v = value; + if (v.convert((QVariant::Type)propertyType)) { + ok = true; + } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) { + QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType); + if (con) { + v = con(value.toString()); + if (v.userType() == propertyType) + ok = true; + } + } + } + if (!ok) { + // the only other option is that they are assigning a single value + // to a sequence type property (eg, an int to a QList<int> property). + // Note that we've already handled single-value assignment to QList<QUrl> properties. + if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) { + QList<int> list; + list << value.toInt(); + v = QVariant::fromValue<QList<int> >(list); + ok = true; + } else if (variantType == QVariant::Double && propertyType == qMetaTypeId<QList<qreal> >()) { + QList<qreal> list; + list << value.toReal(); + v = QVariant::fromValue<QList<qreal> >(list); + ok = true; + } else if (variantType == QVariant::Bool && propertyType == qMetaTypeId<QList<bool> >()) { + QList<bool> list; + list << value.toBool(); + v = QVariant::fromValue<QList<bool> >(list); + ok = true; + } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QList<QString> >()) { + QList<QString> list; + list << value.toString(); + v = QVariant::fromValue<QList<QString> >(list); + ok = true; + } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QStringList>()) { + QStringList list; + list << value.toString(); + v = QVariant::fromValue<QStringList>(list); + ok = true; + } + } + + if (ok) { + void *a[] = { (void *)v.constData(), 0, &status, &flags}; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + } else { + return false; + } + } + + return true; +} + +// Returns true if successful, false if an error description was set on expression +bool QQmlPropertyPrivate::writeBinding(QObject *object, + const QQmlPropertyData &core, + QQmlContextData *context, + QQmlJavaScriptExpression *expression, + v8::Handle<v8::Value> result, bool isUndefined, + WriteFlags flags) +{ + Q_ASSERT(object); + Q_ASSERT(core.coreIndex != -1); + + QQmlEngine *engine = context->engine; + QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); + +#define QUICK_STORE(cpptype, conversion) \ + { \ + cpptype o = (conversion); \ + int status = -1; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, core.coreIndex, argv); \ + return true; \ + } \ + + + if (!isUndefined && !core.isValueTypeVirtual()) { + switch (core.propType) { + case QMetaType::Int: + if (result->IsInt32()) + QUICK_STORE(int, result->Int32Value()) + else if (result->IsNumber()) + QUICK_STORE(int, qRound(result->NumberValue())) + break; + case QMetaType::Double: + if (result->IsNumber()) + QUICK_STORE(double, result->NumberValue()) + break; + case QMetaType::Float: + if (result->IsNumber()) + QUICK_STORE(float, result->NumberValue()) + break; + case QMetaType::QString: + if (result->IsString()) + QUICK_STORE(QString, v8engine->toString(result)) + break; + default: + break; + } + } +#undef QUICK_STORE + + int type = core.isValueTypeVirtual()?core.valueTypePropType:core.propType; + + QQmlJavaScriptExpression::DeleteWatcher watcher(expression); + + QVariant value; + bool isVmeProperty = core.isVMEProperty(); + + if (isUndefined) { + } else if (core.isQList()) { + value = v8engine->toVariant(result, qMetaTypeId<QList<QObject *> >()); + } else if (result->IsNull() && core.isQObject()) { + value = QVariant::fromValue((QObject *)0); + } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { + value = resolvedUrlSequence(v8engine->toVariant(result, qMetaTypeId<QList<QUrl> >()), context); + } else if (!isVmeProperty) { + value = v8engine->toVariant(result, type); + } + + if (expression->hasError()) { + return false; + } else if (isUndefined && core.isResettable()) { + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args); + } else if (isUndefined && type == qMetaTypeId<QVariant>()) { + writeValueProperty(object, engine, core, QVariant(), context, flags); + } else if (isUndefined) { + expression->delayedError()->error.setDescription(QLatin1String("Unable to assign [undefined] to ") + QLatin1String(QMetaType::typeName(type))); + return false; + } else if (result->IsFunction()) { + expression->delayedError()->error.setDescription(QLatin1String("Unable to assign a function to a property.")); + return false; + } else if (isVmeProperty) { + typedef QQmlVMEMetaObject VMEMO; + VMEMO *vmemo = static_cast<VMEMO *>(const_cast<QMetaObject *>(object->metaObject())); + vmemo->setVMEProperty(core.coreIndex, result); + } else if (!writeValueProperty(object, engine, core, value, context, flags)) { + + if (watcher.wasDeleted()) + return true; + + const char *valueType = 0; + if (value.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(value.userType()); + + expression->delayedError()->error.setDescription(QLatin1String("Unable to assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(QMetaType::typeName(type))); + return false; + } + + return true; +} + +bool QQmlPropertyPrivate::writeBinding(const QQmlProperty &that, + QQmlContextData *context, + QQmlJavaScriptExpression *expression, + v8::Handle<v8::Value> result, bool isUndefined, + WriteFlags flags) +{ + QQmlPropertyPrivate *pp = that.d; + + if (!pp) + return true; + + QObject *object = that.object(); + if (!object) + return true; + + return writeBinding(object, pp->core, context, expression, result, isUndefined, flags); +} + +const QMetaObject *QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType) +{ + if (engine) { + return engine->rawMetaObjectForType(userType); + } else { + QQmlType *type = QQmlMetaType::qmlType(userType); + return type?type->baseMetaObject():0; + } +} + +/*! + Sets the property value to \a value and returns true. + Returns false if the property can't be set because the + \a value is the wrong type, for example. + */ +bool QQmlProperty::write(const QVariant &value) const +{ + return QQmlPropertyPrivate::write(*this, value, 0); +} + +/*! + Writes \a value to the \a name property of \a object. This method + is equivalent to: + + \code + QQmlProperty p(object, name); + p.write(value); + \endcode +*/ +bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value) +{ + QQmlProperty p(object, name); + return p.write(value); +} + +/*! + Writes \a value to the \a name property of \a object using the + \l{QQmlContext} {context} \a ctxt. This method is + equivalent to: + + \code + QQmlProperty p(object, name, ctxt); + p.write(value); + \endcode +*/ +bool QQmlProperty::write(QObject *object, + const QString &name, + const QVariant &value, + QQmlContext *ctxt) +{ + QQmlProperty p(object, name, ctxt); + return p.write(value); +} + +/*! + + Writes \a value to the \a name property of \a object using the + environment for instantiating QML components that is provided by + \a engine. This method is equivalent to: + + \code + QQmlProperty p(object, name, engine); + p.write(value); + \endcode +*/ +bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value, + QQmlEngine *engine) +{ + QQmlProperty p(object, name, engine); + return p.write(value); +} + +/*! + Resets the property and returns true if the property is + resettable. If the property is not resettable, nothing happens + and false is returned. +*/ +bool QQmlProperty::reset() const +{ + if (isResettable()) { + void *args[] = { 0 }; + QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args); + return true; + } else { + return false; + } +} + +bool QQmlPropertyPrivate::write(const QQmlProperty &that, + const QVariant &value, WriteFlags flags) +{ + if (!that.d) + return false; + if (that.d->object && that.type() & QQmlProperty::Property && + that.d->core.isValid() && that.isWritable()) + return that.d->writeValueProperty(value, flags); + else + return false; +} + +/*! + Returns true if the property has a change notifier signal, otherwise false. +*/ +bool QQmlProperty::hasNotifySignal() const +{ + if (type() & Property && d->object) { + return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); + } + return false; +} + +/*! + Returns true if the property needs a change notifier signal for bindings + to remain upto date, false otherwise. + + Some properties, such as attached properties or those whose value never + changes, do not require a change notifier. +*/ +bool QQmlProperty::needsNotifySignal() const +{ + return type() & Property && !property().isConstant(); +} + +/*! + Connects the property's change notifier signal to the + specified \a method of the \a dest object and returns + true. Returns false if this metaproperty does not + represent a regular Qt property or if it has no + change notifier signal, or if the \a dest object does + not have the specified \a method. +*/ +bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const +{ + if (!(type() & Property) || !d->object) + return false; + + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + if (prop.hasNotifySignal()) { + return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); + } else { + return false; + } +} + +/*! + Connects the property's change notifier signal to the + specified \a slot of the \a dest object and returns + true. Returns false if this metaproperty does not + represent a regular Qt property or if it has no + change notifier signal, or if the \a dest object does + not have the specified \a slot. +*/ +bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const +{ + if (!(type() & Property) || !d->object) + return false; + + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + if (prop.hasNotifySignal()) { + QByteArray signal(QByteArray("2") + prop.notifySignal().signature()); + return QObject::connect(d->object, signal.constData(), dest, slot); + } else { + return false; + } +} + +/*! + Return the Qt metaobject index of the property. +*/ +int QQmlProperty::index() const +{ + return d ? d->core.coreIndex : -1; +} + +int QQmlPropertyPrivate::valueTypeCoreIndex(const QQmlProperty &that) +{ + return that.d ? that.d->core.getValueTypeCoreIndex() : -1; +} + +/*! + Returns the "property index" for use in bindings. The top 8 bits are the value type + offset, and 0 otherwise. The bottom 24-bits are the regular property index. +*/ +int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that) +{ + if (!that.d) + return -1; + return bindingIndex(that.d->core); +} + +int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that) +{ + int rv = that.coreIndex; + if (rv != -1 && that.isValueTypeVirtual()) + rv = rv | (that.valueTypeCoreIndex << 24); + return rv; +} + +QQmlPropertyData +QQmlPropertyPrivate::saveValueType(const QMetaObject *metaObject, int index, + const QMetaObject *subObject, int subIndex, + QQmlEngine *) +{ + QMetaProperty subProp = subObject->property(subIndex); + + QQmlPropertyData core; + core.load(metaObject->property(index)); + core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp); + core.valueTypeCoreIndex = subIndex; + core.valueTypePropType = subProp.userType(); + + return core; +} + +QQmlProperty +QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data, + QQmlContextData *ctxt) +{ + QQmlProperty prop; + + prop.d = new QQmlPropertyPrivate; + prop.d->object = object; + prop.d->context = ctxt; + prop.d->engine = ctxt?ctxt->engine:0; + + prop.d->core = data; + + return prop; +} + +/*! + Returns true if lhs and rhs refer to the same metaobject data +*/ +bool QQmlPropertyPrivate::equal(const QMetaObject *lhs, const QMetaObject *rhs) +{ + return lhs == rhs || (1 && lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); +} + +/*! + Returns true if from inherits to. +*/ +bool QQmlPropertyPrivate::canConvert(const QMetaObject *from, const QMetaObject *to) +{ + if (from && to == &QObject::staticMetaObject) + return true; + + while (from) { + if (equal(from, to)) + return true; + from = from->superClass(); + } + + return false; +} + +/*! + Return the signal corresponding to \a name +*/ +QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name) +{ + Q_ASSERT(mo); + int methods = mo->methodCount(); + for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal + QMetaMethod method = mo->method(ii); + QByteArray methodName = method.signature(); + int idx = methodName.indexOf('('); + methodName = methodName.left(idx); + + if (methodName == name) + return method; + } + + // If no signal is found, but the signal is of the form "onBlahChanged", + // return the notify signal for the property "Blah" + if (name.endsWith("Changed")) { + QByteArray propName = name.mid(0, name.length() - 7); + int propIdx = mo->indexOfProperty(propName.constData()); + if (propIdx >= 0) { + QMetaProperty prop = mo->property(propIdx); + if (prop.hasNotifySignal()) + return prop.notifySignal(); + } + } + + return QMetaMethod(); +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; +} + +static inline int QMetaObject_properties(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->propertyCount; +} + +static inline void flush_vme_signal(const QObject *object, int index) +{ + QQmlData *data = static_cast<QQmlData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData); + if (data && data->propertyCache) { + QQmlPropertyData *property = data->propertyCache->method(index); + + if (property && property->isVMESignal()) { + const QMetaObject *metaObject = object->metaObject(); + int methodOffset = metaObject->methodOffset(); + + while (methodOffset > index) { + metaObject = metaObject->d.superdata; + methodOffset -= QMetaObject_methods(metaObject); + } + + QQmlVMEMetaObject *vme = + static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(metaObject)); + + vme->connectAliasSignal(index); + } + } +} + +/*! +Connect \a sender \a signal_index to \a receiver \a method_index with the specified +\a type and \a types. This behaves identically to QMetaObject::connect() except that +it connects any lazy "proxy" signal connections set up by QML. + +It is possible that this logic should be moved to QMetaObject::connect(). +*/ +bool QQmlPropertyPrivate::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type, int *types) +{ + flush_vme_signal(sender, signal_index); + flush_vme_signal(receiver, method_index); + + return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); +} + +void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index) +{ + flush_vme_signal(sender, signal_index); +} + +/*! +Return \a metaObject's [super] meta object that provides data for \a property. +*/ +const QMetaObject *QQmlPropertyPrivate::metaObjectForProperty(const QMetaObject *metaObject, int property) +{ + int propertyOffset = metaObject->propertyOffset(); + + while (propertyOffset > property) { + metaObject = metaObject->d.superdata; + propertyOffset -= QMetaObject_properties(metaObject); + } + + return metaObject; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlproperty.h b/src/qml/qml/qqmlproperty.h new file mode 100644 index 0000000000..2c4b2544c1 --- /dev/null +++ b/src/qml/qml/qqmlproperty.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTY_H +#define QQMLPROPERTY_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qmetaobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QObject; +class QVariant; +class QQmlContext; +class QQmlEngine; + +class QQmlPropertyPrivate; +class Q_QML_EXPORT QQmlProperty +{ +public: + enum PropertyTypeCategory { + InvalidCategory, + List, + Object, + Normal + }; + + enum Type { + Invalid, + Property, + SignalProperty + }; + + QQmlProperty(); + ~QQmlProperty(); + + QQmlProperty(QObject *); + QQmlProperty(QObject *, QQmlContext *); + QQmlProperty(QObject *, QQmlEngine *); + + QQmlProperty(QObject *, const QString &); + QQmlProperty(QObject *, const QString &, QQmlContext *); + QQmlProperty(QObject *, const QString &, QQmlEngine *); + + QQmlProperty(const QQmlProperty &); + QQmlProperty &operator=(const QQmlProperty &); + + bool operator==(const QQmlProperty &) const; + + Type type() const; + bool isValid() const; + bool isProperty() const; + bool isSignalProperty() const; + + int propertyType() const; + PropertyTypeCategory propertyTypeCategory() const; + const char *propertyTypeName() const; + + QString name() const; + + QVariant read() const; + static QVariant read(QObject *, const QString &); + static QVariant read(QObject *, const QString &, QQmlContext *); + static QVariant read(QObject *, const QString &, QQmlEngine *); + + bool write(const QVariant &) const; + static bool write(QObject *, const QString &, const QVariant &); + static bool write(QObject *, const QString &, const QVariant &, QQmlContext *); + static bool write(QObject *, const QString &, const QVariant &, QQmlEngine *); + + bool reset() const; + + bool hasNotifySignal() const; + bool needsNotifySignal() const; + bool connectNotifySignal(QObject *dest, const char *slot) const; + bool connectNotifySignal(QObject *dest, int method) const; + + bool isWritable() const; + bool isDesignable() const; + bool isResettable() const; + QObject *object() const; + + int index() const; + QMetaProperty property() const; + QMetaMethod method() const; + +private: + friend class QQmlPropertyPrivate; + QQmlPropertyPrivate *d; +}; +typedef QList<QQmlProperty> QQmlProperties; + +inline uint qHash (const QQmlProperty &key) +{ + return qHash(key.object()) + qHash(key.name()); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLPROPERTY_H diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h new file mode 100644 index 0000000000..0f97a63155 --- /dev/null +++ b/src/qml/qml/qqmlproperty_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTY_P_H +#define QQMLPROPERTY_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 "qqmlproperty.h" + +#include <private/qobject_p.h> +#include <private/qtqmlglobal_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlguard_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlContext; +class QQmlExpression; +class QQmlEnginePrivate; +class QQmlJavaScriptExpression; +class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount +{ +public: + enum WriteFlag { + BypassInterceptor = 0x01, + DontRemoveBinding = 0x02, + RemoveBindingOnAliasWrite = 0x04 + }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + + QQmlContextData *context; + QQmlEngine *engine; + QQmlGuard<QObject> object; + + QQmlPropertyData core; + + bool isNameCached:1; + QString nameCache; + + QQmlPropertyPrivate(); + + inline QQmlContextData *effectiveContext() const; + + void initProperty(QObject *obj, const QString &name); + void initDefault(QObject *obj); + + bool isValueType() const; + int propertyType() const; + QQmlProperty::Type type() const; + QQmlProperty::PropertyTypeCategory propertyTypeCategory() const; + + QVariant readValueProperty(); + bool writeValueProperty(const QVariant &, WriteFlags); + + static const QMetaObject *rawMetaObjectForType(QQmlEnginePrivate *, int); + static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, + const QVariant &value, int flags); + static bool writeValueProperty(QObject *, QQmlEngine *, + const QQmlPropertyData &, + const QVariant &, QQmlContextData *, + WriteFlags flags = 0); + static bool write(QObject *, const QQmlPropertyData &, const QVariant &, + QQmlContextData *, WriteFlags flags = 0); + static void findAliasTarget(QObject *, int, QObject **, int *); + + static QQmlAbstractBinding *setBinding(QObject *, int coreIndex, + int valueTypeIndex /* -1 */, + QQmlAbstractBinding *, + WriteFlags flags = DontRemoveBinding); + static QQmlAbstractBinding *setBindingNoEnable(QObject *, int coreIndex, + int valueTypeIndex /* -1 */, + QQmlAbstractBinding *); + static QQmlAbstractBinding *binding(QObject *, int coreIndex, + int valueTypeIndex /* -1 */); + + static QQmlPropertyData saveValueType(const QMetaObject *, int, + const QMetaObject *, int, + QQmlEngine *); + static QQmlProperty restore(QObject *, + const QQmlPropertyData &, + QQmlContextData *); + + static bool equal(const QMetaObject *, const QMetaObject *); + static bool canConvert(const QMetaObject *from, const QMetaObject *to); + static inline QQmlPropertyPrivate *get(const QQmlProperty &p) { + return p.d; + } + + // "Public" (to QML) methods + static QQmlAbstractBinding *binding(const QQmlProperty &that); + static QQmlAbstractBinding *setBinding(const QQmlProperty &that, + QQmlAbstractBinding *, + WriteFlags flags = DontRemoveBinding); + static QQmlExpression *signalExpression(const QQmlProperty &that); + static QQmlExpression *setSignalExpression(const QQmlProperty &that, + QQmlExpression *) ; + static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); + static bool writeBinding(const QQmlProperty &that, + QQmlContextData *context, + QQmlJavaScriptExpression *expression, + v8::Handle<v8::Value> result, bool isUndefined, + WriteFlags flags); + static bool writeBinding(QObject *, const QQmlPropertyData &, + QQmlContextData *context, + QQmlJavaScriptExpression *expression, + v8::Handle<v8::Value> result, bool isUndefined, + WriteFlags flags); + static int valueTypeCoreIndex(const QQmlProperty &that); + static int bindingIndex(const QQmlProperty &that); + static int bindingIndex(const QQmlPropertyData &that); + static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &); + static bool connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type = 0, int *types = 0); + static const QMetaObject *metaObjectForProperty(const QMetaObject *, int); + static void flushSignal(const QObject *sender, int signal_index); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::WriteFlags) + +QT_END_NAMESPACE + +#endif // QQMLPROPERTY_P_H diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp new file mode 100644 index 0000000000..9b132a4647 --- /dev/null +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -0,0 +1,889 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertycache_p.h" + +#include "qqmlengine_p.h" +#include "qqmlbinding_p.h" +#include <private/qv8engine_p.h> + +#include <private/qmetaobject_p.h> +#include <private/qqmlaccessors_p.h> + +#include <QtCore/qdebug.h> + +Q_DECLARE_METATYPE(QJSValue) +Q_DECLARE_METATYPE(QQmlV8Handle); + +QT_BEGIN_NAMESPACE + +#define Q_INT16_MAX 32767 + +class QQmlPropertyCacheMethodArguments +{ +public: + QQmlPropertyCacheMethodArguments *next; + int arguments[0]; +}; + +// Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick +// to load +static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p) +{ + QQmlPropertyData::Flags flags; + + if (p.isConstant()) + flags |= QQmlPropertyData::IsConstant; + if (p.isWritable()) + flags |= QQmlPropertyData::IsWritable; + if (p.isResettable()) + flags |= QQmlPropertyData::IsResettable; + if (p.isFinal()) + flags |= QQmlPropertyData::IsFinal; + if (p.isEnumType()) + flags |= QQmlPropertyData::IsEnumType; + + return flags; +} + +// Flags that do depend on the property's QMetaProperty::userType() and thus are slow to +// load +static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *engine) +{ + Q_ASSERT(propType != -1); + + QQmlPropertyData::Flags flags; + + if (propType == QMetaType::QObjectStar || propType == QMetaType::QWidgetStar) { + flags |= QQmlPropertyData::IsQObjectDerived; + } else if (propType == QMetaType::QVariant) { + flags |= QQmlPropertyData::IsQVariant; + } else if (propType < (int)QVariant::UserType) { + } else if (propType == qMetaTypeId<QQmlBinding *>()) { + flags |= QQmlPropertyData::IsQmlBinding; + } else if (propType == qMetaTypeId<QJSValue>()) { + flags |= QQmlPropertyData::IsQJSValue; + } else if (propType == qMetaTypeId<QQmlV8Handle>()) { + flags |= QQmlPropertyData::IsV8Handle; + } else { + QQmlMetaType::TypeCategory cat = + engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType) + : QQmlMetaType::typeCategory(propType); + + if (cat == QQmlMetaType::Object) + flags |= QQmlPropertyData::IsQObjectDerived; + else if (cat == QQmlMetaType::List) + flags |= QQmlPropertyData::IsQList; + } + + return flags; +} + +static int metaObjectSignalCount(const QMetaObject *metaObject) +{ + int signalCount = 0; + for (const QMetaObject *obj = metaObject; obj; obj = obj->superClass()) + signalCount += QMetaObjectPrivate::get(obj)->signalCount; + return signalCount; +} + +QQmlPropertyData::Flags +QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine) +{ + return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine); +} + +void QQmlPropertyData::lazyLoad(const QMetaProperty &p, QQmlEngine *engine) +{ + Q_UNUSED(engine); + + coreIndex = p.propertyIndex(); + notifyIndex = p.notifySignalIndex(); + Q_ASSERT(p.revision() <= Q_INT16_MAX); + revision = p.revision(); + + flags = fastFlagsForProperty(p); + + int type = p.type(); + if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) { + propType = type; + flags |= QQmlPropertyData::IsQObjectDerived; + } else if (type == QMetaType::QVariant) { + propType = type; + flags |= QQmlPropertyData::IsQVariant; + } else if (type == QVariant::UserType || type == -1) { + propTypeName = p.typeName(); + flags |= QQmlPropertyData::NotFullyResolved; + } else { + propType = type; + } +} + +void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) +{ + propType = p.userType(); + coreIndex = p.propertyIndex(); + notifyIndex = p.notifySignalIndex(); + flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine); + Q_ASSERT(p.revision() <= Q_INT16_MAX); + revision = p.revision(); +} + +void QQmlPropertyData::load(const QMetaMethod &m) +{ + coreIndex = m.methodIndex(); + arguments = 0; + flags |= IsFunction; + if (m.methodType() == QMetaMethod::Signal) + flags |= IsSignal; + propType = QVariant::Invalid; + + const char *returnType = m.typeName(); + if (returnType) + propType = QMetaType::type(returnType); + + const char *signature = m.signature(); + while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; } + + ++signature; + if (*signature != ')') { + flags |= HasArguments; + if (0 == ::strcmp(signature, "QQmlV8Function*)")) { + flags |= IsV8Function; + } + } + + Q_ASSERT(m.revision() <= Q_INT16_MAX); + revision = m.revision(); +} + +void QQmlPropertyData::lazyLoad(const QMetaMethod &m) +{ + coreIndex = m.methodIndex(); + arguments = 0; + flags |= IsFunction; + if (m.methodType() == QMetaMethod::Signal) + flags |= IsSignal; + propType = QVariant::Invalid; + + const char *returnType = m.typeName(); + if (returnType && *returnType) { + propTypeName = returnType; + flags |= NotFullyResolved; + } + + const char *signature = m.signature(); + while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; } + + ++signature; + if (*signature != ')') { + flags |= HasArguments; + if (0 == ::strcmp(signature, "QQmlV8Function*)")) { + flags |= IsV8Function; + } + } + + Q_ASSERT(m.revision() <= Q_INT16_MAX); + revision = m.revision(); +} + +/*! +Creates a new empty QQmlPropertyCache. +*/ +QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e) +: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), + signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0) +{ + Q_ASSERT(engine); +} + +/*! +Creates a new QQmlPropertyCache of \a metaObject. +*/ +QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e, const QMetaObject *metaObject) +: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), + signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0) +{ + Q_ASSERT(engine); + Q_ASSERT(metaObject); + + update(engine, metaObject); +} + +QQmlPropertyCache::~QQmlPropertyCache() +{ + clear(); + + QQmlPropertyCacheMethodArguments *args = argumentsCache; + while (args) { + QQmlPropertyCacheMethodArguments *next = args->next; + free(args); + args = next; + } + + // We must clear this prior to releasing the parent incase it is a + // linked hash + stringCache.clear(); + if (parent) parent->release(); + parent = 0; + engine = 0; +} + +void QQmlPropertyCache::destroy() +{ + Q_ASSERT(engine || constructor.IsEmpty()); + if (constructor.IsEmpty()) + delete this; + else + QQmlEnginePrivate::deleteInEngineThread(engine, this); +} + +// This is inherited from QQmlCleanup, so it should only clear the things +// that are tied to the specific QQmlEngine. +void QQmlPropertyCache::clear() +{ + qPersistentDispose(constructor); + engine = 0; +} + +QQmlPropertyCache *QQmlPropertyCache::copy(int reserve) +{ + QQmlPropertyCache *cache = new QQmlPropertyCache(engine); + cache->parent = this; + cache->parent->addref(); + cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart; + cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart; + cache->signalHanderIndexCacheStart = signalHandlerIndexCache.count() + signalHanderIndexCacheStart; + cache->stringCache.linkAndReserve(stringCache, reserve); + cache->allowedRevisionCache = allowedRevisionCache; + cache->metaObject = metaObject; + + // We specifically do *NOT* copy the constructor + + return cache; +} + +QQmlPropertyCache *QQmlPropertyCache::copy() +{ + return copy(0); +} + +QQmlPropertyCache * +QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject, + QQmlPropertyData::Flag propertyFlags, + QQmlPropertyData::Flag methodFlags, + QQmlPropertyData::Flag signalFlags) +{ + return copyAndAppend(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags); +} + +QQmlPropertyCache * +QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject, + int revision, + QQmlPropertyData::Flag propertyFlags, + QQmlPropertyData::Flag methodFlags, + QQmlPropertyData::Flag signalFlags) +{ + Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4); + + // Reserve enough space in the name hash for all the methods (including signals), all the + // signal handlers and all the properties. This assumes no name clashes, but this is the + // common case. + QQmlPropertyCache *rv = copy(QMetaObjectPrivate::get(metaObject)->methodCount + + QMetaObjectPrivate::get(metaObject)->signalCount + + QMetaObjectPrivate::get(metaObject)->propertyCount); + + rv->append(engine, metaObject, revision, propertyFlags, methodFlags, signalFlags); + + return rv; +} + +void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, + QQmlPropertyData::Flag propertyFlags, + QQmlPropertyData::Flag methodFlags, + QQmlPropertyData::Flag signalFlags) +{ + append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags); +} + +void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, + int revision, + QQmlPropertyData::Flag propertyFlags, + QQmlPropertyData::Flag methodFlags, + QQmlPropertyData::Flag signalFlags) +{ + Q_UNUSED(revision); + Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache + + this->metaObject = metaObject; + + bool dynamicMetaObject = isDynamicMetaObject(metaObject); + + allowedRevisionCache.append(0); + + int methodCount = metaObject->methodCount(); + Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4); + int signalCount = metaObjectSignalCount(metaObject); + int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount; + + QQmlAccessorProperties::Properties accessorProperties; + + // Special case QObject as we don't want to add a qt_HasQmlAccessors classinfo to it + if (metaObject == &QObject::staticMetaObject) { + accessorProperties = QQmlAccessorProperties::properties(metaObject); + } else if (classInfoCount) { + int classInfoOffset = metaObject->classInfoOffset(); + bool hasFastProperty = false; + for (int ii = 0; ii < classInfoCount; ++ii) { + int idx = ii + classInfoOffset; + + if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) { + hasFastProperty = true; + break; + } + } + + if (hasFastProperty) { + accessorProperties = QQmlAccessorProperties::properties(metaObject); + if (accessorProperties.count == 0) + qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not " + "installed property accessors", metaObject->className()); + } else { +#ifndef QT_NO_DEBUG + accessorProperties = QQmlAccessorProperties::properties(metaObject); + if (accessorProperties.count != 0) + qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing " + "FastProperty class info", metaObject->className()); +#endif + } + } + + // qMax(defaultMethods, methodOffset) to block the signals and slots of QObject::staticMetaObject + // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot. + int methodOffset = qMax(QObject::staticMetaObject.methodCount(), metaObject->methodOffset()); + int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount; + + // update() should have reserved enough space in the vector that this doesn't cause a realloc + // and invalidate the stringCache. + methodIndexCache.resize(methodCount - methodIndexCacheStart); + signalHandlerIndexCache.resize(signalCount - signalHanderIndexCacheStart); + int signalHandlerIndex = signalOffset; + for (int ii = methodOffset; ii < methodCount; ++ii) { + QMetaMethod m = metaObject->method(ii); + if (m.access() == QMetaMethod::Private) + continue; + + // Extract method name + const char *signature = m.signature(); + const char *cptr = signature; + char utf8 = 0; + while (*cptr != '(') { + Q_ASSERT(*cptr != 0); + utf8 |= *cptr & 0x80; + ++cptr; + } + + QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart]; + QQmlPropertyData *sigdata = 0; + + data->lazyLoad(m); + + if (data->isSignal()) + data->flags |= signalFlags; + else + data->flags |= methodFlags; + + if (!dynamicMetaObject) + data->flags |= QQmlPropertyData::IsDirect; + + Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); + data->metaObjectOffset = allowedRevisionCache.count() - 1; + + if (data->isSignal()) { + sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHanderIndexCacheStart]; + *sigdata = *data; + sigdata->flags |= QQmlPropertyData::IsSignalHandler; + } + + QQmlPropertyData *old = 0; + + if (utf8) { + QHashedString methodName(QString::fromUtf8(signature, cptr - signature)); + if (QQmlPropertyData **it = stringCache.value(methodName)) + old = *it; + stringCache.insert(methodName, data); + + if (data->isSignal()) { + QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1)); + stringCache.insert(on, sigdata); + ++signalHandlerIndex; + } + } else { + QHashedCStringRef methodName(signature, cptr - signature); + if (QQmlPropertyData **it = stringCache.value(methodName)) + old = *it; + stringCache.insert(methodName, data); + + if (data->isSignal()) { + int length = methodName.length(); + + QVarLengthArray<char, 128> str(length+3); + str[0] = 'o'; + str[1] = 'n'; + str[2] = toupper(signature[0]); + if (length > 1) + memcpy(&str[3], &signature[1], length - 1); + str[length + 2] = '\0'; + + QHashedString on(QString::fromLatin1(str.data())); + stringCache.insert(on, sigdata); + ++signalHandlerIndex; + } + } + + if (old) { + // We only overload methods in the same class, exactly like C++ + if (old->isFunction() && old->coreIndex >= methodOffset) + data->flags |= QQmlPropertyData::IsOverload; + data->overrideIndexIsProperty = !old->isFunction(); + data->overrideIndex = old->coreIndex; + } + } + + int propCount = metaObject->propertyCount(); + int propOffset = metaObject->propertyOffset(); + + // update() should have reserved enough space in the vector that this doesn't cause a realloc + // and invalidate the stringCache. + propertyIndexCache.resize(propCount - propertyIndexCacheStart); + for (int ii = propOffset; ii < propCount; ++ii) { + QMetaProperty p = metaObject->property(ii); + if (!p.isScriptable()) + continue; + + const char *str = p.name(); + char utf8 = 0; + const char *cptr = str; + while (*cptr != 0) { + utf8 |= *cptr & 0x80; + ++cptr; + } + + QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart]; + + data->lazyLoad(p, engine); + data->flags |= propertyFlags; + + if (!dynamicMetaObject) + data->flags |= QQmlPropertyData::IsDirect; + + Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); + data->metaObjectOffset = allowedRevisionCache.count() - 1; + + QQmlPropertyData *old = 0; + + if (utf8) { + QHashedString propName(QString::fromUtf8(str, cptr - str)); + if (QQmlPropertyData **it = stringCache.value(propName)) + old = *it; + stringCache.insert(propName, data); + } else { + QHashedCStringRef propName(str, cptr - str); + if (QQmlPropertyData **it = stringCache.value(propName)) + old = *it; + stringCache.insert(propName, data); + } + + QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str); + + // Fast properties may not be overrides or revisioned + Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0)); + + if (accessorProperty) { + data->flags |= QQmlPropertyData::HasAccessors; + data->accessors = accessorProperty->accessors; + data->accessorData = accessorProperty->data; + } else if (old) { + data->overrideIndexIsProperty = !old->isFunction(); + data->overrideIndex = old->coreIndex; + } + } +} + +void QQmlPropertyCache::resolve(QQmlPropertyData *data) const +{ + Q_ASSERT(data->notFullyResolved()); + + data->propType = QMetaType::type(data->propTypeName); + + if (!data->isFunction()) + data->flags |= flagsForPropertyType(data->propType, engine); + + data->flags &= ~QQmlPropertyData::NotFullyResolved; +} + +void QQmlPropertyCache::updateRecur(QQmlEngine *engine, const QMetaObject *metaObject) +{ + if (!metaObject) + return; + + updateRecur(engine, metaObject->superClass()); + + append(engine, metaObject); +} + +void QQmlPropertyCache::update(QQmlEngine *engine, const QMetaObject *metaObject) +{ + Q_ASSERT(engine); + Q_ASSERT(metaObject); + Q_ASSERT(stringCache.isEmpty()); + + // Preallocate enough space in the index caches for all the properties/methods/signals that + // are not cached in a parent cache so that the caches never need to be reallocated as this + // would invalidate pointers stored in the stringCache. + int pc = metaObject->propertyCount(); + int mc = metaObject->methodCount(); + int sc = metaObjectSignalCount(metaObject); + propertyIndexCache.reserve(pc - propertyIndexCacheStart); + methodIndexCache.reserve(mc - methodIndexCacheStart); + signalHandlerIndexCache.reserve(sc - signalHanderIndexCacheStart); + + // Reserve enough space in the stringCache for all properties/methods/signals including those + // cached in a parent cache. + stringCache.reserve(pc + mc + sc); + + updateRecur(engine,metaObject); +} + +QQmlPropertyData * +QQmlPropertyCache::property(int index) const +{ + if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count())) + return 0; + + if (index < propertyIndexCacheStart) + return parent->property(index); + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart)); + if (rv->notFullyResolved()) resolve(rv); + return rv; +} + +QQmlPropertyData * +QQmlPropertyCache::method(int index) const +{ + if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) + return 0; + + if (index < methodIndexCacheStart) + return parent->method(index); + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart)); + if (rv->notFullyResolved()) resolve(rv); + return rv; +} + +QQmlPropertyData * +QQmlPropertyCache::property(const QHashedStringRef &str) const +{ + QQmlPropertyData **rv = stringCache.value(str); + if (rv && (*rv)->notFullyResolved()) resolve(*rv); + return rv?*rv:0; +} + +QQmlPropertyData * +QQmlPropertyCache::property(const QHashedCStringRef &str) const +{ + QQmlPropertyData **rv = stringCache.value(str); + if (rv && (*rv)->notFullyResolved()) resolve(*rv); + return rv?*rv:0; +} + +QQmlPropertyData * +QQmlPropertyCache::property(const QString &str) const +{ + QQmlPropertyData **rv = stringCache.value(str); + if (rv && (*rv)->notFullyResolved()) resolve(*rv); + return rv?*rv:0; +} + +QString QQmlPropertyData::name(QObject *object) +{ + if (!object) + return QString(); + + return name(object->metaObject()); +} + +QString QQmlPropertyData::name(const QMetaObject *metaObject) +{ + if (!metaObject || coreIndex == -1) + return QString(); + + if (flags & IsFunction) { + QMetaMethod m = metaObject->method(coreIndex); + + QString name = QString::fromUtf8(m.signature()); + int parenIdx = name.indexOf(QLatin1Char('(')); + if (parenIdx != -1) + name = name.left(parenIdx); + return name; + } else { + QMetaProperty p = metaObject->property(coreIndex); + return QString::fromUtf8(p.name()); + } +} + +QStringList QQmlPropertyCache::propertyNames() const +{ + QStringList keys; + for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) + keys.append(iter.key()); + return keys; +} + +static int EnumType(const QMetaObject *meta, const QByteArray &str) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = str.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = str.left(scopeIdx); + name = str.mid(scopeIdx + 2); + } else { + name = str; + } + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) + return QVariant::Int; + } + return QVariant::Invalid; +} + +// Returns an array of the arguments for method \a index. The first entry in the array +// is the number of arguments. +int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, + QVarLengthArray<int, 9> &dummy, + QByteArray *unknownTypeError) +{ + Q_ASSERT(object && index >= 0); + + QQmlData *ddata = QQmlData::get(object, false); + + if (ddata && ddata->propertyCache) { + typedef QQmlPropertyCacheMethodArguments A; + + QQmlPropertyCache *c = ddata->propertyCache; + 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) + return static_cast<A *>(rv->arguments)->arguments; + + const QMetaObject *metaObject = object->metaObject(); + QMetaMethod m = metaObject->method(index); + QList<QByteArray> argTypeNames = m.parameterTypes(); + + A *args = static_cast<A *>(malloc(sizeof(A) + (argTypeNames.count() + 1) * sizeof(int))); + args->arguments[0] = argTypeNames.count(); + + for (int ii = 0; ii < argTypeNames.count(); ++ii) { + int type = QMetaType::type(argTypeNames.at(ii)); + if (type == QVariant::Invalid) + type = EnumType(object->metaObject(), argTypeNames.at(ii)); + if (type == QVariant::Invalid) { + if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); + free(args); + return 0; + } + args->arguments[ii + 1] = type; + } + + rv->arguments = args; + args->next = c->argumentsCache; + c->argumentsCache = args; + return static_cast<A *>(rv->arguments)->arguments; + + } else { + QMetaMethod m = object->metaObject()->method(index); + QList<QByteArray> argTypeNames = m.parameterTypes(); + dummy.resize(argTypeNames.count() + 1); + dummy[0] = argTypeNames.count(); + + for (int ii = 0; ii < argTypeNames.count(); ++ii) { + int type = QMetaType::type(argTypeNames.at(ii)); + if (type == QVariant::Invalid) + type = EnumType(object->metaObject(), argTypeNames.at(ii)); + if (type == QVariant::Invalid) { + if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); + return 0; + } + dummy[ii + 1] = type; + } + + return dummy.data(); + } +} + +QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, + const QString &property) +{ + Q_ASSERT(metaObject); + + QQmlPropertyData rv; + { + const QMetaObject *cmo = metaObject; + const QByteArray propertyName = property.toUtf8(); + while (cmo) { + int idx = cmo->indexOfProperty(propertyName); + if (idx != -1) { + QMetaProperty p = cmo->property(idx); + if (p.isScriptable()) { + rv.load(p); + return rv; + } else { + while (cmo && cmo->propertyOffset() >= idx) + cmo = cmo->superClass(); + } + } else { + cmo = 0; + } + } + } + + int methodCount = metaObject->methodCount(); + int defaultMethods = QObject::staticMetaObject.methodCount(); + for (int ii = methodCount - 1; ii >= defaultMethods; --ii) { + // >=defaultMethods to block the signals and slots of QObject::staticMetaObject + // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot. + QMetaMethod m = metaObject->method(ii); + if (m.access() == QMetaMethod::Private) + continue; + QString methodName = QString::fromUtf8(m.signature()); + + int parenIdx = methodName.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + QStringRef methodNameRef = methodName.leftRef(parenIdx); + + if (methodNameRef == property) { + rv.load(m); + return rv; + } + } + + return rv; +} + +inline const QString &qQmlPropertyCacheToString(const QString &string) +{ + return string; +} + +inline QString qQmlPropertyCacheToString(const QHashedV8String &string) +{ + return QV8Engine::toStringStatic(string.string()); +} + +template<typename T> +QQmlPropertyData * +qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj, + const T &name, QQmlPropertyData &local) +{ + QQmlPropertyCache *cache = 0; + + if (engine) { + QQmlData *ddata = QQmlData::get(obj); + + if (ddata && ddata->propertyCache) { + cache = ddata->propertyCache; + } else if (engine) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + cache = ep->cache(obj); + if (cache) { + ddata = QQmlData::get(obj, true); + cache->addref(); + ddata->propertyCache = cache; + } + } + } + + QQmlPropertyData *rv = 0; + + if (cache) { + rv = cache->property(name); + } else { + local = qQmlPropertyCacheCreate(obj->metaObject(), + qQmlPropertyCacheToString(name)); + if (local.isValid()) + rv = &local; + } + + return rv; +} + +QQmlPropertyData * +QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, + const QHashedV8String &name, QQmlPropertyData &local) +{ + return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, local); +} + +QQmlPropertyData * +QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, + const QString &name, QQmlPropertyData &local) +{ + return qQmlPropertyCacheProperty<QString>(engine, obj, name, local); +} + +static inline const QMetaObjectPrivate *priv(const uint* data) +{ return reinterpret_cast<const QMetaObjectPrivate*>(data); } + +bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo) +{ + return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h new file mode 100644 index 0000000000..095ee79867 --- /dev/null +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYCACHE_P_H +#define QQMLPROPERTYCACHE_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 "qqmlcleanup_p.h" +#include "qqmlnotifier_p.h" + +#include <private/qhashedstring_p.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QMetaProperty; +class QV8QObjectWrapper; +class QQmlEngine; +class QQmlPropertyData; +class QQmlAccessors; +class QQmlPropertyCacheMethodArguments; + +// 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. +class QQmlPropertyRawData +{ +public: + enum Flag { + NoFlags = 0x00000000, + ValueTypeFlagMask = 0x0000FFFF, // Flags in valueTypeFlags must fit in this mask + + // Can apply to all properties, except IsFunction + IsConstant = 0x00000001, // Has CONST flag + IsWritable = 0x00000002, // Has WRITE function + IsResettable = 0x00000004, // Has RESET function + IsAlias = 0x00000008, // Is a QML alias to another property + IsFinal = 0x00000010, // Has FINAL flag + IsDirect = 0x00000020, // Exists on a C++ QMetaObject + HasAccessors = 0x00000040, // Has property accessors + + // These are mutualy exclusive + IsFunction = 0x00000080, // Is an invokable + IsQObjectDerived = 0x00000100, // Property type is a QObject* derived type + IsEnumType = 0x00000200, // Property type is an enum + IsQList = 0x00000400, // Property type is a QML list + IsQmlBinding = 0x00000800, // Property type is a QQmlBinding* + IsQJSValue = 0x00001000, // Property type is a QScriptValue + IsV8Handle = 0x00002000, // Property type is a QQmlV8Handle + IsVMEProperty = 0x00004000, // Property type is a "var" property of VMEMO + IsValueTypeVirtual = 0x00008000, // Property is a value type "virtual" property + IsQVariant = 0x00010000, // Property is a QVariant + + // Apply only to IsFunctions + IsVMEFunction = 0x00020000, // Function was added by QML + HasArguments = 0x00040000, // Function takes arguments + IsSignal = 0x00080000, // Function is a signal + IsVMESignal = 0x00100000, // Signal was added by QML + IsV8Function = 0x00200000, // Function takes QQmlV8Function* args + IsSignalHandler = 0x00400000, // Function is a signal handler + IsOverload = 0x00800000, // Function is an overload of another function + + // Internal QQmlPropertyCache flags + NotFullyResolved = 0x01000000 // True if the type data is to be lazily resolved + }; + Q_DECLARE_FLAGS(Flags, Flag) + + Flags getFlags() const { return Flag(flags); } + void setFlags(Flags f) { flags = f; } + + bool isValid() const { return coreIndex != -1; } + + bool isConstant() const { return flags & IsConstant; } + bool isWritable() const { return flags & IsWritable; } + bool isResettable() const { return flags & IsResettable; } + bool isAlias() const { return flags & IsAlias; } + bool isFinal() const { return flags & IsFinal; } + bool isDirect() const { return flags & IsDirect; } + bool hasAccessors() const { return flags & HasAccessors; } + bool isFunction() const { return flags & IsFunction; } + bool isQObject() const { return flags & IsQObjectDerived; } + bool isEnum() const { return flags & IsEnumType; } + bool isQList() const { return flags & IsQList; } + bool isQmlBinding() const { return flags & IsQmlBinding; } + bool isQJSValue() const { return flags & IsQJSValue; } + bool isV8Handle() const { return flags & IsV8Handle; } + bool isVMEProperty() const { return flags & IsVMEProperty; } + bool isValueTypeVirtual() const { return flags & IsValueTypeVirtual; } + bool isQVariant() const { return flags & IsQVariant; } + 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 isV8Function() const { return flags & IsV8Function; } + bool isSignalHandler() const { return flags & IsSignalHandler; } + bool isOverload() const { return flags & IsOverload; } + + bool hasOverride() const { return !(flags & IsValueTypeVirtual) && + !(flags & HasAccessors) && + overrideIndex >= 0; } + + // Returns -1 if not a value type virtual property + inline int getValueTypeCoreIndex() const; + + // Returns the "encoded" index for use with bindings. Encoding is: + // coreIndex | (valueTypeCoreIndex << 24) + inline int encodedIndex() const; + + union { + int propType; // When !NotFullyResolved + const char *propTypeName; // When NotFullyResolved + }; + int coreIndex; + union { + int notifyIndex; // When !IsFunction + void *arguments; // When IsFunction && HasArguments + }; + + union { + struct { // When !HasAccessors + qint16 revision; + qint16 metaObjectOffset; + + union { + struct { // When IsValueTypeVirtual + quint16 valueTypeFlags; // flags of the access property on the value type proxy + // object + quint8 valueTypePropType; // The QVariant::Type of access property on the value + // type proxy object + quint8 valueTypeCoreIndex; // The prop index of the access property on the value + // type proxy object + }; + + struct { // When !IsValueTypeVirtual + uint overrideIndexIsProperty : 1; + signed int overrideIndex : 31; + }; + }; + }; + struct { // When HasAccessors + QQmlAccessors *accessors; + intptr_t accessorData; + }; + }; + +private: + friend class QQmlPropertyData; + friend class QQmlPropertyCache; + quint32 flags; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags); + +class QQmlPropertyData : public QQmlPropertyRawData +{ +public: + inline QQmlPropertyData(); + inline QQmlPropertyData(const QQmlPropertyRawData &); + + inline bool operator==(const QQmlPropertyRawData &); + + static Flags flagsForProperty(const QMetaProperty &, QQmlEngine *engine = 0); + void load(const QMetaProperty &, QQmlEngine *engine = 0); + void load(const QMetaMethod &); + QString name(QObject *); + QString name(const QMetaObject *); + +private: + friend class QQmlPropertyCache; + void lazyLoad(const QMetaProperty &, QQmlEngine *engine = 0); + void lazyLoad(const QMetaMethod &); + bool notFullyResolved() const { return flags & NotFullyResolved; } +}; + +class Q_QML_EXPORT QQmlPropertyCache : public QQmlRefCount, public QQmlCleanup +{ +public: + QQmlPropertyCache(QQmlEngine *); + QQmlPropertyCache(QQmlEngine *, const QMetaObject *); + virtual ~QQmlPropertyCache(); + + void update(QQmlEngine *, const QMetaObject *); + + QQmlPropertyCache *copy(); + + QQmlPropertyCache *copyAndAppend(QQmlEngine *, const QMetaObject *, + QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + QQmlPropertyCache *copyAndAppend(QQmlEngine *, const QMetaObject *, int revision, + QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + + void append(QQmlEngine *, const QMetaObject *, + QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + void append(QQmlEngine *, const QMetaObject *, int revision, + QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, + QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); + + inline QQmlPropertyData *property(const QHashedV8String &) const; + QQmlPropertyData *property(const QHashedStringRef &) const; + QQmlPropertyData *property(const QHashedCStringRef &) const; + QQmlPropertyData *property(const QString &) const; + QQmlPropertyData *property(int) const; + QQmlPropertyData *method(int) const; + QStringList propertyNames() const; + + inline QQmlPropertyData *overrideData(QQmlPropertyData *) const; + inline bool isAllowedInRevision(QQmlPropertyData *) const; + + inline QQmlEngine *qmlEngine() const; + static QQmlPropertyData *property(QQmlEngine *, QObject *, const QString &, + QQmlPropertyData &); + static QQmlPropertyData *property(QQmlEngine *, QObject *, const QHashedV8String &, + QQmlPropertyData &); + static int *methodParameterTypes(QObject *, int index, QVarLengthArray<int, 9> &dummy, + QByteArray *unknownTypeError); + + static bool isDynamicMetaObject(const QMetaObject *); +protected: + virtual void destroy(); + virtual void clear(); + +private: + friend class QQmlEnginePrivate; + friend class QV8QObjectWrapper; + + inline QQmlPropertyCache *copy(int reserve); + + // Implemented in v8/qv8qobjectwrapper.cpp + v8::Local<v8::Object> newQObject(QObject *, QV8Engine *); + + typedef QVector<QQmlPropertyData> IndexCache; + typedef QStringHash<QQmlPropertyData *> StringCache; + typedef QVector<int> AllowedRevisionCache; + + void resolve(QQmlPropertyData *) const; + void updateRecur(QQmlEngine *, const QMetaObject *); + + QQmlEngine *engine; + + QQmlPropertyCache *parent; + int propertyIndexCacheStart; + int methodIndexCacheStart; + int signalHanderIndexCacheStart; + + IndexCache propertyIndexCache; + IndexCache methodIndexCache; + IndexCache signalHandlerIndexCache; + StringCache stringCache; + AllowedRevisionCache allowedRevisionCache; + v8::Persistent<v8::Function> constructor; + + const QMetaObject *metaObject; + QQmlPropertyCacheMethodArguments *argumentsCache; +}; + +QQmlPropertyData::QQmlPropertyData() +{ + propType = 0; + coreIndex = -1; + notifyIndex = -1; + overrideIndexIsProperty = false; + overrideIndex = -1; + revision = 0; + metaObjectOffset = -1; + flags = 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 && + (!isValueTypeVirtual() || + (valueTypeCoreIndex == other.valueTypeCoreIndex && + valueTypePropType == other.valueTypePropType)); +} + +int QQmlPropertyRawData::getValueTypeCoreIndex() const +{ + return isValueTypeVirtual()?valueTypeCoreIndex:-1; +} + +int QQmlPropertyRawData::encodedIndex() const +{ + return isValueTypeVirtual()?(coreIndex | (valueTypeCoreIndex << 24)):coreIndex; +} + +QQmlPropertyData * +QQmlPropertyCache::overrideData(QQmlPropertyData *data) const +{ + if (!data->hasOverride()) + return 0; + + if (data->overrideIndexIsProperty) + return property(data->overrideIndex); + else + return method(data->overrideIndex); +} + +bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const +{ + return (data->hasAccessors() || (data->metaObjectOffset == -1 && data->revision == 0)) || + (allowedRevisionCache[data->metaObjectOffset] >= data->revision); +} + +QQmlEngine *QQmlPropertyCache::qmlEngine() const +{ + return engine; +} + +QQmlPropertyData *QQmlPropertyCache::property(const QHashedV8String &str) const +{ + QQmlPropertyData **rv = stringCache.value(str); + if (rv && (*rv)->notFullyResolved()) resolve(*rv); + return rv?*rv:0; +} + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHE_P_H diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor.cpp b/src/qml/qml/qqmlpropertyvalueinterceptor.cpp new file mode 100644 index 0000000000..331b90da5c --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalueinterceptor.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyvalueinterceptor_p.h" + +#include "qqml.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlPropertyValueInterceptor + \brief The QQmlPropertyValueInterceptor class is inherited by property interceptors such as Behavior. + \internal + + This class intercepts property writes, allowing for custom handling. For example, Behavior uses this + interception to provide a default animation for all changes to a property's value. + */ + +/*! + Constructs a QQmlPropertyValueInterceptor. +*/ +QQmlPropertyValueInterceptor::QQmlPropertyValueInterceptor() +{ +} + +QQmlPropertyValueInterceptor::~QQmlPropertyValueInterceptor() +{ +} + +/*! + \fn void QQmlPropertyValueInterceptor::setTarget(const QQmlProperty &property) + Set the target \a property for the value interceptor. This method will + be called by the QML engine when assigning a value interceptor. +*/ + +/*! + \fn void QQmlPropertyValueInterceptor::write(const QVariant &value) + This method will be called when a new \a value is assigned to the property being intercepted. +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h new file mode 100644 index 0000000000..f8b8643a00 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYVALUEINTERCEPTOR_P_H +#define QQMLPROPERTYVALUEINTERCEPTOR_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_BEGIN_NAMESPACE + +class QQmlProperty; +class Q_QML_EXPORT QQmlPropertyValueInterceptor +{ +public: + QQmlPropertyValueInterceptor(); + virtual ~QQmlPropertyValueInterceptor(); + virtual void setTarget(const QQmlProperty &property) = 0; + virtual void write(const QVariant &value) = 0; +}; +Q_DECLARE_INTERFACE(QQmlPropertyValueInterceptor, "com.trolltech.qml.QQmlPropertyValueInterceptor") + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYVALUEINTERCEPTOR_P_H diff --git a/src/qml/qml/qqmlpropertyvaluesource.cpp b/src/qml/qml/qqmlpropertyvaluesource.cpp new file mode 100644 index 0000000000..bd1ca05b81 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvaluesource.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyvaluesource.h" + +#include "qqml.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlPropertyValueSource + \brief The QQmlPropertyValueSource class is an interface for property value sources such as animations and bindings. + + See \l{Property Value Sources} for information on writing custom property + value sources. + */ + +/*! + Constructs a QQmlPropertyValueSource. +*/ +QQmlPropertyValueSource::QQmlPropertyValueSource() +{ +} + +/*! + Destroys the value source. +*/ +QQmlPropertyValueSource::~QQmlPropertyValueSource() +{ +} + +/*! + \fn void QQmlPropertyValueSource::setTarget(const QQmlProperty &property) + Set the target \a property for the value source. This method will + be called by the QML engine when assigning a value source. +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyvaluesource.h b/src/qml/qml/qqmlpropertyvaluesource.h new file mode 100644 index 0000000000..910b23877e --- /dev/null +++ b/src/qml/qml/qqmlpropertyvaluesource.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYVALUESOURCE_H +#define QQMLPROPERTYVALUESOURCE_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlProperty; +class Q_QML_EXPORT QQmlPropertyValueSource +{ +public: + QQmlPropertyValueSource(); + virtual ~QQmlPropertyValueSource(); + virtual void setTarget(const QQmlProperty &) = 0; +}; +Q_DECLARE_INTERFACE(QQmlPropertyValueSource, "com.trolltech.qml.QQmlPropertyValueSource") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLPROPERTYVALUESOURCE_H diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp new file mode 100644 index 0000000000..55bf67739d --- /dev/null +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlproxymetaobject_p.h" +#include "qqmlproperty_p.h" + +QT_BEGIN_NAMESPACE + +QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList) +: metaObjects(mList), proxies(0), parent(0), object(obj) +{ + *static_cast<QMetaObject *>(this) = *metaObjects->first().metaObject; + + QObjectPrivate *op = QObjectPrivate::get(obj); + if (op->metaObject) + parent = static_cast<QAbstractDynamicMetaObject*>(op->metaObject); + + op->metaObject = this; +} + +QQmlProxyMetaObject::~QQmlProxyMetaObject() +{ + if (parent) + delete parent; + parent = 0; + + if (proxies) + delete [] proxies; + proxies = 0; +} + +int QQmlProxyMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if ((c == QMetaObject::ReadProperty || + c == QMetaObject::WriteProperty) && + id >= metaObjects->last().propertyOffset) { + + for (int ii = 0; ii < metaObjects->count(); ++ii) { + const ProxyData &data = metaObjects->at(ii); + if (id >= data.propertyOffset) { + if (!proxies) { + proxies = new QObject*[metaObjects->count()]; + ::memset(proxies, 0, + sizeof(QObject *) * metaObjects->count()); + } + + if (!proxies[ii]) { + QObject *proxy = data.createFunc(object); + const QMetaObject *metaObject = proxy->metaObject(); + proxies[ii] = proxy; + + int localOffset = data.metaObject->methodOffset(); + int methodOffset = metaObject->methodOffset(); + int methods = metaObject->methodCount() - methodOffset; + + // ### - Can this be done more optimally? + for (int jj = 0; jj < methods; ++jj) { + QMetaMethod method = + metaObject->method(jj + methodOffset); + if (method.methodType() == QMetaMethod::Signal) + QQmlPropertyPrivate::connect(proxy, methodOffset + jj, object, localOffset + jj); + } + } + + int proxyOffset = proxies[ii]->metaObject()->propertyOffset(); + int proxyId = id - data.propertyOffset + proxyOffset; + + return proxies[ii]->qt_metacall(c, proxyId, a); + } + } + } else if (c == QMetaObject::InvokeMetaMethod && + id >= metaObjects->last().methodOffset) { + QMetaMethod m = object->metaObject()->method(id); + if (m.methodType() == QMetaMethod::Signal) { + QMetaObject::activate(object, id, a); + return -1; + } + } + + if (parent) + return parent->metaCall(c, id, a); + else + return object->qt_metacall(c, id, a); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h new file mode 100644 index 0000000000..ec9df42f7f --- /dev/null +++ b/src/qml/qml/qqmlproxymetaobject_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROXYMETAOBJECT_P_H +#define QQMLPROXYMETAOBJECT_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/qmetaobjectbuilder_p.h> +#include "qqml.h" + +#include <QtCore/QMetaObject> +#include <QtCore/QObject> + +#include <private/qobject_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlProxyMetaObject : public QAbstractDynamicMetaObject +{ +public: + struct ProxyData { + typedef QObject *(*CreateFunc)(QObject *); + QMetaObject *metaObject; + CreateFunc createFunc; + int propertyOffset; + int methodOffset; + }; + + QQmlProxyMetaObject(QObject *, QList<ProxyData> *); + virtual ~QQmlProxyMetaObject(); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + QList<ProxyData> *metaObjects; + QObject **proxies; + + QAbstractDynamicMetaObject *parent; + QObject *object; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLPROXYMETAOBJECT_P_H + diff --git a/src/qml/qml/qqmlrewrite.cpp b/src/qml/qml/qqmlrewrite.cpp new file mode 100644 index 0000000000..828f7bf641 --- /dev/null +++ b/src/qml/qml/qqmlrewrite.cpp @@ -0,0 +1,441 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlrewrite_p.h" + +#include <private/qqmlglobal_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP); + +namespace QQmlRewrite { + +bool SharedBindingTester::isSharable(const QString &code) +{ + Engine engine; + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) + return false; + + return isSharable(parser.statement()); +} + +bool SharedBindingTester::isSharable(AST::Node *node) +{ + _sharable = true; + AST::Node::acceptChild(node, this); + return _sharable; +} + +QString RewriteBinding::operator()(const QString &code, bool *ok, bool *sharable) +{ + Engine engine; + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) { + if (ok) *ok = false; + return QString(); + } else { + if (ok) *ok = true; + if (sharable) { + SharedBindingTester tester; + *sharable = tester.isSharable(parser.statement()); + } + } + return rewrite(code, 0, parser.statement()); +} + +QString RewriteBinding::operator()(QQmlJS::AST::Node *node, const QString &code, bool *sharable) +{ + if (!node) + return code; + + if (sharable) { + SharedBindingTester tester; + *sharable = tester.isSharable(node); + } + + QQmlJS::AST::ExpressionNode *expression = node->expressionCast(); + QQmlJS::AST::Statement *statement = node->statementCast(); + if(!expression && !statement) + return code; + + TextWriter w; + _writer = &w; + _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin(); + _inLoop = 0; + _code = &code; + + accept(node); + + unsigned startOfStatement = 0; + unsigned endOfStatement = (expression ? expression->lastSourceLocation().end() : statement->lastSourceLocation().end()) - _position; + + QString startString = QLatin1String("(function ") + _name + QLatin1String("() { "); + if (expression) + startString += QLatin1String("return "); + _writer->replace(startOfStatement, 0, startString); + _writer->replace(endOfStatement, 0, QLatin1String(" })")); + + if (rewriteDump()) { + qWarning() << "============================================================="; + qWarning() << "Rewrote:"; + qWarning() << qPrintable(code); + } + + QString codeCopy = code; + w.write(&codeCopy); + + if (rewriteDump()) { + qWarning() << "To:"; + qWarning() << qPrintable(codeCopy); + qWarning() << "============================================================="; + } + + return codeCopy; +} + +void RewriteBinding::accept(AST::Node *node) +{ + AST::Node::acceptChild(node, this); +} + +QString RewriteBinding::rewrite(QString code, unsigned position, + AST::Statement *node) +{ + TextWriter w; + _writer = &w; + _position = position; + _inLoop = 0; + _code = &code; + + accept(node); + + unsigned startOfStatement = node->firstSourceLocation().begin() - _position; + unsigned endOfStatement = node->lastSourceLocation().end() - _position; + + _writer->replace(startOfStatement, 0, QLatin1String("(function ") + _name + QLatin1String("() { ")); + _writer->replace(endOfStatement, 0, QLatin1String(" })")); + + if (rewriteDump()) { + qWarning() << "============================================================="; + qWarning() << "Rewrote:"; + qWarning() << qPrintable(code); + } + + w.write(&code); + + if (rewriteDump()) { + qWarning() << "To:"; + qWarning() << qPrintable(code); + qWarning() << "============================================================="; + } + + return code; +} + +bool RewriteBinding::visit(AST::Block *ast) +{ + for (AST::StatementList *it = ast->statements; it; it = it->next) { + if (! it->next) { + // we need to rewrite only the last statement of a block. + accept(it->statement); + } + } + + return false; +} + +bool RewriteBinding::visit(AST::ExpressionStatement *ast) +{ + if (! _inLoop) { + unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position; + _writer->replace(startOfExpressionStatement, 0, QLatin1String("return ")); + } + + return false; +} + +bool RewriteBinding::visit(AST::StringLiteral *ast) +{ + /* When rewriting the code for bindings, we have to remove ILLEGAL JS tokens like newlines. + They're still in multi-line strings, because the QML parser allows them, but we have to + rewrite them to be JS compliant. + + For performance reasons, we don't go for total correctness. \r is only replaced if a + \n was found (since most line endings are \n or \r\n) and QChar::LineSeparator is not + even considered. QTBUG-24064. + + Note that rewriteSignalHandler has a function just like this one, for the same reason. + */ + + unsigned startOfString = ast->firstSourceLocation().begin() + 1 - _position; + unsigned stringLength = ast->firstSourceLocation().length - 2; + + int lastIndex = -1; + bool foundNewLine = false; + QStringRef subStr(_code, startOfString, stringLength); + while (true) { + lastIndex = subStr.indexOf(QLatin1Char('\n'), lastIndex + 1); + if (lastIndex == -1) + break; + foundNewLine = true; + _writer->replace(startOfString+lastIndex, 1, QLatin1String("\\n")); + } + + if (foundNewLine) { + while (true) { + lastIndex = subStr.indexOf(QLatin1Char('\r'), lastIndex + 1); + if (lastIndex == -1) + break; + _writer->replace(startOfString+lastIndex, 1, QLatin1String("\\r")); + } + } + + return false; +} + +bool RewriteBinding::visit(AST::DoWhileStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::DoWhileStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::WhileStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::WhileStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::ForStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::ForStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::LocalForStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::LocalForStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::ForEachStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::ForEachStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::LocalForEachStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::LocalForEachStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::CaseBlock *ast) +{ + // Process the initial sequence of the case clauses. + for (AST::CaseClauses *it = ast->clauses; it; it = it->next) { + // Return the value of the last statement in the block, if this is the last `case clause' + // of the switch statement. + bool returnTheValueOfLastStatement = (it->next == 0) && (ast->defaultClause == 0) && (ast->moreClauses == 0); + + if (AST::CaseClause *clause = it->clause) { + accept(clause->expression); + rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement); + } + } + + // Process the default case clause + if (ast->defaultClause) { + // Return the value of the last statement in the block, if this is the last `case clause' + // of the switch statement. + bool rewriteTheLastStatement = (ast->moreClauses == 0); + + rewriteCaseStatements(ast->defaultClause->statements, rewriteTheLastStatement); + } + + // Process trailing `case clauses' + for (AST::CaseClauses *it = ast->moreClauses; it; it = it->next) { + // Return the value of the last statement in the block, if this is the last `case clause' + // of the switch statement. + bool returnTheValueOfLastStatement = (it->next == 0); + + if (AST::CaseClause *clause = it->clause) { + accept(clause->expression); + rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement); + } + } + + return false; +} + +void RewriteBinding::rewriteCaseStatements(AST::StatementList *statements, bool rewriteTheLastStatement) +{ + for (AST::StatementList *it = statements; it; it = it->next) { + if (it->next && AST::cast<AST::BreakStatement *>(it->next->statement) != 0) { + // The value of the first statement followed by a `break'. + accept(it->statement); + break; + } else if (!it->next) { + if (rewriteTheLastStatement) + accept(it->statement); + else if (AST::Block *block = AST::cast<AST::Block *>(it->statement)) + rewriteCaseStatements(block->statements, rewriteTheLastStatement); + } + } +} + +void RewriteSignalHandler::accept(AST::Node *node) +{ + AST::Node::acceptChild(node, this); +} + +bool RewriteSignalHandler::visit(AST::StringLiteral *ast) +{ + unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position; + _strStarts << startOfExpressionStatement + 1; + _strLens << ast->firstSourceLocation().length - 2; + + return false; +} + +void RewriteSignalHandler::rewriteMultilineStrings(QString &code) +{ + QList<int> replaceR, replaceN; + for (int i=0; i < _strStarts.count(); i++) { + QStringRef curSubstr = QStringRef(&code, _strStarts[i], _strLens[i]); + int lastIndex = -1; + while (true) { + lastIndex = curSubstr.indexOf(QLatin1Char('\n'), lastIndex + 1); + if (lastIndex == -1) + break; + replaceN << _strStarts[i]+lastIndex; + } + + if (replaceN.count()) { + while (true) { + lastIndex = curSubstr.indexOf(QLatin1Char('\r'), lastIndex + 1); + if (lastIndex == -1) + break; + replaceR << _strStarts[i]+lastIndex; + } + } + } + for (int ii = replaceN.count() - 1; ii >= 0; ii--) + code.replace(replaceN[ii], 1, QLatin1String("\\n")); + if (replaceR.count()) + for (int ii = replaceR.count() - 1; ii >= 0; ii--) + code.replace(replaceR[ii], 1, QLatin1String("\\r")); +} + +QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name) +{ + if (rewriteDump()) { + qWarning() << "============================================================="; + qWarning() << "Rewrote:"; + qWarning() << qPrintable(code); + } + + QQmlJS::AST::ExpressionNode *expression = node->expressionCast(); + QQmlJS::AST::Statement *statement = node->statementCast(); + if (!expression && !statement) + return code; + + _strStarts.clear(); + _strLens.clear(); + _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin(); + accept(node); + + QString rewritten = code; + rewriteMultilineStrings(rewritten); + + rewritten = QStringLiteral("(function ") + name + QStringLiteral("() { ") + rewritten + QStringLiteral(" })"); + + if (rewriteDump()) { + qWarning() << "To:"; + qWarning() << qPrintable(rewritten); + qWarning() << "============================================================="; + } + + return rewritten; +} + +} // namespace QQmlRewrite + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlrewrite_p.h b/src/qml/qml/qqmlrewrite_p.h new file mode 100644 index 0000000000..e915d797df --- /dev/null +++ b/src/qml/qml/qqmlrewrite_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLREWRITE_P_H +#define QQMLREWRITE_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/textwriter_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsmemorypool_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlRewrite { +using namespace QQmlJS; + +class SharedBindingTester : protected AST::Visitor +{ + bool _sharable; +public: + bool isSharable(const QString &code); + bool isSharable(AST::Node *Node); + + virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; } + virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; } + virtual bool visit(AST::CallExpression *) { _sharable = false; return false; } +}; + +class RewriteBinding: protected AST::Visitor +{ + unsigned _position; + TextWriter *_writer; + QString _name; + const QString *_code; + +public: + QString operator()(const QString &code, bool *ok = 0, bool *sharable = 0); + QString operator()(QQmlJS::AST::Node *node, const QString &code, bool *sharable = 0); + + //name of the function: used for the debugger + void setName(const QString &name) { _name = name; } + +protected: + using AST::Visitor::visit; + + void accept(AST::Node *node); + QString rewrite(QString code, unsigned position, AST::Statement *node); + void rewriteCaseStatements(AST::StatementList *statements, bool rewriteTheLastStatement); + + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::ExpressionStatement *ast); + + virtual bool visit(AST::DoWhileStatement *ast); + virtual void endVisit(AST::DoWhileStatement *ast); + + virtual bool visit(AST::WhileStatement *ast); + virtual void endVisit(AST::WhileStatement *ast); + + virtual bool visit(AST::ForStatement *ast); + virtual void endVisit(AST::ForStatement *ast); + + virtual bool visit(AST::LocalForStatement *ast); + virtual void endVisit(AST::LocalForStatement *ast); + + virtual bool visit(AST::ForEachStatement *ast); + virtual void endVisit(AST::ForEachStatement *ast); + + virtual bool visit(AST::LocalForEachStatement *ast); + virtual void endVisit(AST::LocalForEachStatement *ast); + + virtual bool visit(AST::CaseBlock *ast); + +private: + int _inLoop; +}; + +class RewriteSignalHandler: protected AST::Visitor +{ + QList<int> _strStarts; + QList<int> _strLens; + int _position; + +public: + QString operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name); + +protected: + void rewriteMultilineStrings(QString &code); + + using AST::Visitor::visit; + void accept(AST::Node *node); + virtual bool visit(AST::StringLiteral *ast); +}; + +} // namespace QQmlRewrite + +QT_END_NAMESPACE + +#endif // QQMLREWRITE_P_H + diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp new file mode 100644 index 0000000000..d1c2faa138 --- /dev/null +++ b/src/qml/qml/qqmlscript.cpp @@ -0,0 +1,1700 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlscript_p.h" + +#include "parser/qqmljsengine_p.h" +#include "parser/qqmljsparser_p.h" +#include "parser/qqmljslexer_p.h" +#include "parser/qqmljsmemorypool_p.h" +#include "parser/qqmljsastvisitor_p.h" +#include "parser/qqmljsast_p.h" +#include <private/qqmlrewrite_p.h> + +#include <QStack> +#include <QCoreApplication> +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; +using namespace QQmlScript; + +// +// Parser IR classes +// +QQmlScript::Object::Object() +: type(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1), + componentCompileState(0), nextAliasingObject(0), nextIdObject(0) +{ +} + +QQmlScript::Object::~Object() +{ + if (synthCache) synthCache->release(); +} + +void Object::setBindingBit(int b) +{ + while (bindingBitmask.size() < 4 * (1 + b / 32)) + bindingBitmask.append(char(0)); + + quint32 *bits = (quint32 *)bindingBitmask.data(); + bits[b / 32] |= (1 << (b % 32)); +} + +const QMetaObject *Object::metaObject() const +{ + if (!metadata.isEmpty() && metatype) + return &extObject; + else + return metatype; +} + +QQmlScript::Property *Object::getDefaultProperty() +{ + if (!defaultProperty) { + defaultProperty = pool()->New<Property>(); + defaultProperty->parent = this; + } + return defaultProperty; +} + +void QQmlScript::Object::addValueProperty(Property *p) +{ + valueProperties.append(p); +} + +void QQmlScript::Object::addSignalProperty(Property *p) +{ + signalProperties.append(p); +} + +void QQmlScript::Object::addAttachedProperty(Property *p) +{ + attachedProperties.append(p); +} + +void QQmlScript::Object::addGroupedProperty(Property *p) +{ + groupedProperties.append(p); +} + +void QQmlScript::Object::addValueTypeProperty(Property *p) +{ + valueTypeProperties.append(p); +} + +void QQmlScript::Object::addScriptStringProperty(Property *p) +{ + scriptStringProperties.append(p); +} + +// This lookup is optimized for missing, and having to create a new property. +Property *QQmlScript::Object::getProperty(const QHashedStringRef &name, bool create) +{ + if (create) { + quint32 h = name.hash(); + if (propertiesHashField.testAndSet(h)) { + for (Property *p = properties.first(); p; p = properties.next(p)) { + if (p->name() == name) + return p; + } + } + + Property *property = pool()->New<Property>(); + property->parent = this; + property->_name = name; + property->isDefault = false; + properties.prepend(property); + return property; + } else { + for (Property *p = properties.first(); p; p = properties.next(p)) { + if (p->name() == name) + return p; + } + } + + return 0; +} + +Property *QQmlScript::Object::getProperty(const QStringRef &name, bool create) +{ + return getProperty(QHashedStringRef(name), create); +} + +Property *QQmlScript::Object::getProperty(const QString &name, bool create) +{ + for (Property *p = properties.first(); p; p = properties.next(p)) { + if (p->name() == name) + return p; + } + + if (create) { + Property *property = pool()->New<Property>(); + property->parent = this; + property->_name = QStringRef(pool()->NewString(name)); + propertiesHashField.testAndSet(property->_name.hash()); + property->isDefault = false; + properties.prepend(property); + return property; + } else { + return 0; + } +} + +QQmlScript::Object::DynamicProperty::DynamicProperty() +: isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0), + resolvedCustomTypeName(0) +{ +} + +QQmlScript::Object::DynamicSignal::DynamicSignal() +: nextSignal(0) +{ +} + +// Returns length in utf8 bytes +int QQmlScript::Object::DynamicSignal::parameterTypesLength() const +{ + int rv = 0; + for (int ii = 0; ii < parameterTypes.count(); ++ii) + rv += parameterTypes.at(ii).length(); + return rv; +} + +// Returns length in utf8 bytes +int QQmlScript::Object::DynamicSignal::parameterNamesLength() const +{ + int rv = 0; + for (int ii = 0; ii < parameterNames.count(); ++ii) + rv += parameterNames.at(ii).utf8length(); + return rv; +} + +QQmlScript::Object::DynamicSlot::DynamicSlot() +: nextSlot(0) +{ +} + +int QQmlScript::Object::DynamicSlot::parameterNamesLength() const +{ + int rv = 0; + for (int ii = 0; ii < parameterNames.count(); ++ii) + rv += parameterNames.at(ii).length(); + return rv; +} + +QQmlScript::Property::Property() +: parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false), + isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false), + scriptStringScope(-1), nextMainProperty(0), nextProperty(0) +{ +} + +QQmlScript::Object *QQmlScript::Property::getValue(const LocationSpan &l) +{ + if (!value) { value = pool()->New<Object>(); value->location = l; } + return value; +} + +void QQmlScript::Property::addValue(Value *v) +{ + values.append(v); +} + +void QQmlScript::Property::addOnValue(Value *v) +{ + onValues.append(v); +} + +bool QQmlScript::Property::isEmpty() const +{ + return !value && values.isEmpty() && onValues.isEmpty(); +} + +QQmlScript::Value::Value() +: type(Unknown), object(0), bindingReference(0), nextValue(0) +{ +} + +QQmlScript::Variant::Variant() +: t(Invalid) +{ +} + +QQmlScript::Variant::Variant(const Variant &o) +: t(o.t), d(o.d), asWritten(o.asWritten) +{ +} + +QQmlScript::Variant::Variant(bool v) +: t(Boolean), b(v) +{ +} + +QQmlScript::Variant::Variant(double v, const QStringRef &asWritten) +: t(Number), d(v), asWritten(asWritten) +{ +} + +QQmlScript::Variant::Variant(QQmlJS::AST::StringLiteral *v) +: t(String), l(v) +{ +} + +QQmlScript::Variant::Variant(const QStringRef &asWritten, QQmlJS::AST::Node *n) +: t(Script), n(n), asWritten(asWritten) +{ +} + +QQmlScript::Variant &QQmlScript::Variant::operator=(const Variant &o) +{ + t = o.t; + d = o.d; + asWritten = o.asWritten; + return *this; +} + +QQmlScript::Variant::Type QQmlScript::Variant::type() const +{ + return t; +} + +bool QQmlScript::Variant::asBoolean() const +{ + return b; +} + +QString QQmlScript::Variant::asString() const +{ + if (t == String) { + return l->value.toString(); + } else { + return asWritten.toString(); + } +} + +double QQmlScript::Variant::asNumber() const +{ + return d; +} + +//reverse of Lexer::singleEscape() +QString escapedString(const QString &string) +{ + QString tmp = QLatin1String("\""); + for (int i = 0; i < string.length(); ++i) { + const QChar &c = string.at(i); + switch(c.unicode()) { + case 0x08: + tmp += QLatin1String("\\b"); + break; + case 0x09: + tmp += QLatin1String("\\t"); + break; + case 0x0A: + tmp += QLatin1String("\\n"); + break; + case 0x0B: + tmp += QLatin1String("\\v"); + break; + case 0x0C: + tmp += QLatin1String("\\f"); + break; + case 0x0D: + tmp += QLatin1String("\\r"); + break; + case 0x22: + tmp += QLatin1String("\\\""); + break; + case 0x27: + tmp += QLatin1String("\\\'"); + break; + case 0x5C: + tmp += QLatin1String("\\\\"); + break; + default: + tmp += c; + break; + } + } + tmp += QLatin1Char('\"'); + return tmp; +} + +QString QQmlScript::Variant::asScript() const +{ + switch(type()) { + default: + case Invalid: + return QString(); + case Boolean: + return b?QLatin1String("true"):QLatin1String("false"); + case Number: + if (asWritten.isEmpty()) + return QString::number(d); + else + return asWritten.toString(); + case String: + return escapedString(asString()); + case Script: + if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) { + return i->name.toString(); + } else + return asWritten.toString(); + } +} + +QQmlJS::AST::Node *QQmlScript::Variant::asAST() const +{ + if (type() == Script) + return n; + else + return 0; +} + +bool QQmlScript::Variant::isStringList() const +{ + if (isString()) + return true; + + if (type() != Script || !n) + return false; + + AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n); + if (!array) + return false; + + AST::ElementList *elements = array->elements; + + while (elements) { + + if (!AST::cast<AST::StringLiteral *>(elements->expression)) + return false; + + elements = elements->next; + } + + return true; +} + +QStringList QQmlScript::Variant::asStringList() const +{ + QStringList rv; + if (isString()) { + rv << asString(); + return rv; + } + + AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n); + if (!array) + return rv; + + AST::ElementList *elements = array->elements; + while (elements) { + + AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression); + if (!string) + return QStringList(); + rv.append(string->value.toString()); + + elements = elements->next; + } + + return rv; +} + +// +// Actual parser classes +// +void QQmlScript::Import::extractVersion(int *maj, int *min) const +{ + *maj = -1; *min = -1; + + if (!version.isEmpty()) { + int dot = version.indexOf(QLatin1Char('.')); + if (dot < 0) { + *maj = version.toInt(); + *min = 0; + } else { + *maj = version.left(dot).toInt(); + *min = version.mid(dot+1).toInt(); + } + } +} + +namespace { + +class ProcessAST: protected AST::Visitor +{ + struct State { + State() : object(0), property(0) {} + State(QQmlScript::Object *o) : object(o), property(0) {} + State(QQmlScript::Object *o, Property *p) : object(o), property(p) {} + + QQmlScript::Object *object; + Property *property; + }; + + struct StateStack : public QStack<State> + { + void pushObject(QQmlScript::Object *obj) + { + push(State(obj)); + } + + void pushProperty(const QString &name, const LocationSpan &location) + { + const State &state = top(); + if (state.property) { + State s(state.property->getValue(location), + state.property->getValue(location)->getProperty(name)); + s.property->location = location; + push(s); + } else { + State s(state.object, state.object->getProperty(name)); + + s.property->location = location; + push(s); + } + } + + void pushProperty(const QStringRef &name, const LocationSpan &location) + { + const State &state = top(); + if (state.property) { + State s(state.property->getValue(location), + state.property->getValue(location)->getProperty(name)); + s.property->location = location; + push(s); + } else { + State s(state.object, state.object->getProperty(name)); + + s.property->location = location; + push(s); + } + } + }; + +public: + ProcessAST(QQmlScript::Parser *parser); + virtual ~ProcessAST(); + + void operator()(const QString &code, AST::Node *node); + +protected: + + QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment, + const QString &objectType, + AST::SourceLocation typeLocation, + LocationSpan location, + AST::UiObjectInitializer *initializer = 0); + + QQmlScript::Variant getVariant(AST::Statement *stmt); + QQmlScript::Variant getVariant(AST::ExpressionNode *expr); + + LocationSpan location(AST::SourceLocation start, AST::SourceLocation end); + LocationSpan location(AST::UiQualifiedId *); + + using AST::Visitor::visit; + using AST::Visitor::endVisit; + + virtual bool visit(AST::UiProgram *node); + virtual bool visit(AST::UiImport *node); + virtual bool visit(AST::UiObjectDefinition *node); + virtual bool visit(AST::UiPublicMember *node); + virtual bool visit(AST::UiObjectBinding *node); + + virtual bool visit(AST::UiScriptBinding *node); + virtual bool visit(AST::UiArrayBinding *node); + virtual bool visit(AST::UiSourceElement *node); + + void accept(AST::Node *node); + + QString asString(AST::UiQualifiedId *node) const; + + const State state() const; + QQmlScript::Object *currentObject() const; + Property *currentProperty() const; + + QString qualifiedNameId() const; + + QString textAt(const AST::SourceLocation &loc) const + { return _contents->mid(loc.offset, loc.length); } + + QStringRef textRefAt(const AST::SourceLocation &loc) const + { return QStringRef(_contents, loc.offset, loc.length); } + + QString textAt(const AST::SourceLocation &first, + const AST::SourceLocation &last) const + { return _contents->mid(first.offset, last.offset + last.length - first.offset); } + + QStringRef textRefAt(const AST::SourceLocation &first, + const AST::SourceLocation &last) const + { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); } + + QString asString(AST::ExpressionNode *expr) + { + if (! expr) + return QString(); + + return textAt(expr->firstSourceLocation(), expr->lastSourceLocation()); + } + + QStringRef asStringRef(AST::ExpressionNode *expr) + { + if (! expr) + return QStringRef(); + + return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation()); + } + + QString asString(AST::Statement *stmt) + { + if (! stmt) + return QString(); + + QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()); + s += QLatin1Char('\n'); + return s; + } + + QStringRef asStringRef(AST::Statement *stmt) + { + if (! stmt) + return QStringRef(); + + return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()); + } + +private: + QQmlScript::Parser *_parser; + StateStack _stateStack; + QStringList _scope; + const QString *_contents; +}; + +ProcessAST::ProcessAST(QQmlScript::Parser *parser) + : _parser(parser) +{ +} + +ProcessAST::~ProcessAST() +{ +} + +void ProcessAST::operator()(const QString &code, AST::Node *node) +{ + _contents = &code; + accept(node); +} + +void ProcessAST::accept(AST::Node *node) +{ + AST::Node::acceptChild(node, this); +} + +const ProcessAST::State ProcessAST::state() const +{ + if (_stateStack.isEmpty()) + return State(); + + return _stateStack.back(); +} + +QQmlScript::Object *ProcessAST::currentObject() const +{ + return state().object; +} + +Property *ProcessAST::currentProperty() const +{ + return state().property; +} + +QString ProcessAST::qualifiedNameId() const +{ + return _scope.join(QLatin1String("/")); +} + +QString ProcessAST::asString(AST::UiQualifiedId *node) const +{ + QString s; + + for (AST::UiQualifiedId *it = node; it; it = it->next) { + s.append(it->name.toString()); + + if (it->next) + s.append(QLatin1Char('.')); + } + + return s; +} + +QQmlScript::Object * +ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName, + bool onAssignment, + const QString &objectType, + AST::SourceLocation typeLocation, + LocationSpan location, + AST::UiObjectInitializer *initializer) +{ + int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.')); + + // With no preceding qualification, first char is at (-1 + 1) == 0 + bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper(); + + int propertyCount = 0; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name, + this->location(name)); + } + + if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return 0; + } + + if (!isType) { + + // Is the identifier qualified by a namespace? + int namespaceLength = 0; + if (lastTypeDot > 0) { + const QString qualifier(objectType.left(lastTypeDot)); + + for (int ii = 0; ii < _parser->_imports.count(); ++ii) { + const QQmlScript::Import &import = _parser->_imports.at(ii); + if (import.qualifier == qualifier) { + // The qualifier is a namespace - expect a type here + namespaceLength = qualifier.length() + 1; + break; + } + } + } + + if (propertyCount || !currentObject() || namespaceLength) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Expected type name")); + error.setLine(typeLocation.startLine); + error.setColumn(typeLocation.startColumn + namespaceLength); + _parser->_errors << error; + return 0; + } + + LocationSpan loc = ProcessAST::location(typeLocation, typeLocation); + if (propertyName) + loc = ProcessAST::location(propertyName); + + _stateStack.pushProperty(objectType, loc); + accept(initializer); + _stateStack.pop(); + + return 0; + + } else { + // Class + + QString resolvableObjectType = objectType; + if (lastTypeDot >= 0) + resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/')); + + QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>(); + + QQmlScript::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType); + obj->type = typeRef->id; + + typeRef->refObjects.append(obj); + + // XXX this doesn't do anything (_scope never builds up) + _scope.append(resolvableObjectType); + obj->typeName = qualifiedNameId(); + _scope.removeLast(); + + obj->location = location; + + if (propertyCount) { + Property *prop = currentProperty(); + QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>(); + v->object = obj; + v->location = obj->location; + if (onAssignment) + prop->addOnValue(v); + else + prop->addValue(v); + + while (propertyCount--) + _stateStack.pop(); + + } else { + + if (! _parser->tree()) { + _parser->setTree(obj); + } else { + const State state = _stateStack.top(); + QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>(); + v->object = obj; + v->location = obj->location; + if (state.property) { + state.property->addValue(v); + } else { + Property *defaultProp = state.object->getDefaultProperty(); + if (defaultProp->location.start.line == -1) { + defaultProp->location = v->location; + defaultProp->location.end = defaultProp->location.start; + defaultProp->location.range.length = 0; + } + defaultProp->addValue(v); + } + } + } + + _stateStack.pushObject(obj); + accept(initializer); + _stateStack.pop(); + + return obj; + } +} + +LocationSpan ProcessAST::location(AST::UiQualifiedId *id) +{ + return location(id->identifierToken, id->identifierToken); +} + +LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end) +{ + LocationSpan rv; + rv.start.line = start.startLine; + rv.start.column = start.startColumn; + rv.end.line = end.startLine; + rv.end.column = end.startColumn + end.length - 1; + rv.range.offset = start.offset; + rv.range.length = end.offset + end.length - start.offset; + return rv; +} + +// UiProgram: UiImportListOpt UiObjectMemberList ; +bool ProcessAST::visit(AST::UiProgram *node) +{ + accept(node->imports); + accept(node->members->member); + return false; +} + +// UiImport: T_IMPORT T_STRING_LITERAL ; +bool ProcessAST::visit(AST::UiImport *node) +{ + QString uri; + QQmlScript::Import import; + + if (!node->fileName.isNull()) { + uri = node->fileName.toString(); + + if (uri.endsWith(QLatin1String(".js"))) { + import.type = QQmlScript::Import::Script; + } else { + import.type = QQmlScript::Import::File; + } + } else { + import.type = QQmlScript::Import::Library; + uri = asString(node->importUri); + } + + AST::SourceLocation startLoc = node->importToken; + AST::SourceLocation endLoc = node->semicolonToken; + + // Qualifier + if (!node->importId.isNull()) { + import.qualifier = node->importId.toString(); + if (!import.qualifier.at(0).isUpper()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + if (import.qualifier == QLatin1String("Qt")) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + + // Check for script qualifier clashes + bool isScript = import.type == QQmlScript::Import::Script; + for (int ii = 0; ii < _parser->_imports.count(); ++ii) { + const QQmlScript::Import &other = _parser->_imports.at(ii); + bool otherIsScript = other.type == QQmlScript::Import::Script; + + if ((isScript || otherIsScript) && import.qualifier == other.qualifier) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique.")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + } + + } else if (import.type == QQmlScript::Import::Script) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier")); + error.setLine(node->fileNameToken.startLine); + error.setColumn(node->fileNameToken.startColumn); + _parser->_errors << error; + return false; + } + + if (node->versionToken.isValid()) { + import.version = textAt(node->versionToken); + } else if (import.type == QQmlScript::Import::Library) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + + + import.location = location(startLoc, endLoc); + import.uri = uri; + + _parser->_imports << import; + + return false; +} + +bool ProcessAST::visit(AST::UiPublicMember *node) +{ + static const struct TypeNameToType { + const char *name; + int nameLength; + Object::DynamicProperty::Type type; + const char *qtName; + int qtNameLength; + } propTypeNameToTypes[] = { + { "int", strlen("int"), Object::DynamicProperty::Int, "int", strlen("int") }, + { "bool", strlen("bool"), Object::DynamicProperty::Bool, "bool", strlen("bool") }, + { "double", strlen("double"), Object::DynamicProperty::Real, "double", strlen("double") }, + { "real", strlen("real"), Object::DynamicProperty::Real, "qreal", strlen("qreal") }, + { "string", strlen("string"), Object::DynamicProperty::String, "QString", strlen("QString") }, + { "url", strlen("url"), Object::DynamicProperty::Url, "QUrl", strlen("QUrl") }, + { "color", strlen("color"), Object::DynamicProperty::Color, "QColor", strlen("QColor") }, + // Internally QTime, QDate and QDateTime are all supported. + // To be more consistent with JavaScript we expose only + // QDateTime as it matches closely with the Date JS type. + // We also call it "date" to match. + // { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") }, + // { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") }, + { "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") }, + { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") }, + { "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") } + }; + static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + if(node->type == AST::UiPublicMember::Signal) { + Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>(); + signal->name = node->name; + + AST::UiParameterList *p = node->parameters; + int paramLength = 0; + while (p) { paramLength++; p = p->next; } + p = node->parameters; + + if (paramLength) { + signal->parameterTypes = _parser->_pool.NewRawList<QHashedCStringRef>(paramLength); + signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength); + } + + int index = 0; + while (p) { + const QStringRef &memberType = p->type; + + const TypeNameToType *type = 0; + for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { + const TypeNameToType *t = propTypeNameToTypes + typeIndex; + if (t->nameLength == memberType.length() && + QHashedString::compare(memberType.constData(), t->name, t->nameLength)) { + type = t; + break; + } + } + + if (!type) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + _parser->_errors << error; + return false; + } + + signal->parameterTypes[index] = QHashedCStringRef(type->qtName, type->qtNameLength); + signal->parameterNames[index] = QHashedStringRef(p->name); + p = p->next; + index++; + } + + signal->location = location(node->typeToken, node->semicolonToken); + _stateStack.top().object->dynamicSignals.append(signal); + } else { + const QStringRef &memberType = node->memberType; + const QStringRef &name = node->name; + + bool typeFound = false; + Object::DynamicProperty::Type type; + + if ((unsigned)memberType.length() == strlen("alias") && + QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) { + type = Object::DynamicProperty::Alias; + typeFound = true; + } + + for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { + const TypeNameToType *t = propTypeNameToTypes + ii; + if (t->nameLength == memberType.length() && + QHashedString::compare(memberType.constData(), t->name, t->nameLength)) { + type = t->type; + typeFound = true; + } + } + + if (!typeFound && memberType.at(0).isUpper()) { + const QStringRef &typeModifier = node->typeModifier; + + if (typeModifier.isEmpty()) { + type = Object::DynamicProperty::Custom; + } else if((unsigned)typeModifier.length() == strlen("list") && + QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) { + type = Object::DynamicProperty::CustomList; + } else { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + _parser->_errors << error; + return false; + } + typeFound = true; + } else if (!node->typeModifier.isNull()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + _parser->_errors << error; + return false; + } + + if(!typeFound) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + _parser->_errors << error; + return false; + } + + Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>(); + property->isDefaultProperty = node->isDefaultMember; + property->isReadOnly = node->isReadonlyMember; + property->type = type; + property->nameLocation.line = node->identifierToken.startLine; + property->nameLocation.column = node->identifierToken.startColumn; + if (type >= Object::DynamicProperty::Custom) { + QQmlScript::TypeReference *typeRef = + _parser->findOrCreateType(memberType.toString()); + typeRef->refObjects.append(_stateStack.top().object); + property->customType = memberType; + } + + property->name = QHashedStringRef(name); + property->location = location(node->firstSourceLocation(), + node->lastSourceLocation()); + + if (node->statement) { // default value + property->defaultValue = _parser->_pool.New<Property>(); + property->defaultValue->parent = _stateStack.top().object; + property->defaultValue->location = + location(node->statement->firstSourceLocation(), + node->statement->lastSourceLocation()); + QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>(); + value->location = location(node->statement->firstSourceLocation(), + node->statement->lastSourceLocation()); + value->value = getVariant(node->statement); + property->defaultValue->values.append(value); + } + + _stateStack.top().object->dynamicProperties.append(property); + + // process QML-like initializers (e.g. property Object o: Object {}) + accept(node->binding); + } + + return false; +} + + +// UiObjectMember: UiQualifiedId UiObjectInitializer ; +bool ProcessAST::visit(AST::UiObjectDefinition *node) +{ + LocationSpan l = location(node->firstSourceLocation(), + node->lastSourceLocation()); + + const QString objectType = asString(node->qualifiedTypeNameId); + const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; + + defineObjectBinding(/*propertyName = */ 0, false, objectType, + typeLocation, l, node->initializer); + + return false; +} + + +// UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +bool ProcessAST::visit(AST::UiObjectBinding *node) +{ + LocationSpan l = location(node->qualifiedTypeNameId->identifierToken, + node->initializer->rbraceToken); + + const QString objectType = asString(node->qualifiedTypeNameId); + const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; + + defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType, + typeLocation, l, node->initializer); + + return false; +} + +QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt) +{ + if (stmt) { + if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt)) + return getVariant(exprStmt->expression); + + return QQmlScript::Variant(asStringRef(stmt), stmt); + } + + return QQmlScript::Variant(); +} + +QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr) +{ + if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) { + return QQmlScript::Variant(lit); + } else if (expr->kind == AST::Node::Kind_TrueLiteral) { + return QQmlScript::Variant(true); + } else if (expr->kind == AST::Node::Kind_FalseLiteral) { + return QQmlScript::Variant(false); + } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) { + return QQmlScript::Variant(lit->value, asStringRef(expr)); + } else { + + if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) { + if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) { + return QQmlScript::Variant(-lit->value, asStringRef(expr)); + } + } + + return QQmlScript::Variant(asStringRef(expr), expr); + } +} + + +// UiObjectMember: UiQualifiedId T_COLON Statement ; +bool ProcessAST::visit(AST::UiScriptBinding *node) +{ + int propertyCount = 0; + AST::UiQualifiedId *propertyName = node->qualifiedId; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name, + location(name)); + } + + Property *prop = currentProperty(); + + if (!prop->values.isEmpty()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return 0; + } + + QQmlScript::Variant primitive; + + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) { + primitive = getVariant(stmt->expression); + } else { // do binding + primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement); + } + + prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset; + prop->location.range.offset = node->qualifiedId->identifierToken.offset; + QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>(); + v->value = primitive; + v->location = location(node->statement->firstSourceLocation(), + node->statement->lastSourceLocation()); + + prop->addValue(v); + + while (propertyCount--) + _stateStack.pop(); + + return false; +} + +// UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +bool ProcessAST::visit(AST::UiArrayBinding *node) +{ + int propertyCount = 0; + AST::UiQualifiedId *propertyName = node->qualifiedId; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name, + location(name)); + } + + Property* prop = currentProperty(); + + if (!prop->values.isEmpty()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return false; + } + + accept(node->members); + + // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range: + prop->listValueRange.offset = node->lbracketToken.offset; + prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset; + + while (propertyCount--) + _stateStack.pop(); + + return false; +} + +bool ProcessAST::visit(AST::UiSourceElement *node) +{ + QQmlScript::Object *obj = currentObject(); + + if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) { + + Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>(); + slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation()); + + AST::FormalParameterList *f = funDecl->formals; + while (f) { + slot->parameterNames << f->name.toUtf8(); + f = f->next; + } + + AST::SourceLocation loc = funDecl->rparenToken; + loc.offset = loc.end(); + loc.startColumn += 1; + QString body = textAt(loc, funDecl->rbraceToken); + slot->name = funDecl->name; + slot->body = body; + obj->dynamicSlots.append(slot); + + } else { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); + error.setLine(node->firstSourceLocation().startLine); + error.setColumn(node->firstSourceLocation().startColumn); + _parser->_errors << error; + } + return false; +} + +} // end of anonymous namespace + + +QQmlScript::Parser::Parser() +: root(0), data(0) +{ + +} + +QQmlScript::Parser::~Parser() +{ + clear(); +} + +namespace QQmlScript { +class ParserJsASTData +{ +public: + ParserJsASTData(const QString &filename) + : filename(filename) {} + + QString filename; + Engine engine; +}; +} + +bool QQmlScript::Parser::parse(const QByteArray &qmldata, const QUrl &url, + const QString &urlString) +{ + clear(); + + if (urlString.isEmpty()) { + _scriptFile = url.toString(); + } else { + // Q_ASSERT(urlString == url.toString()); + _scriptFile = urlString; + } + + QTextStream stream(qmldata, QIODevice::ReadOnly); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + QString *code = _pool.NewString(stream.readAll()); + + data = new QQmlScript::ParserJsASTData(_scriptFile); + + Lexer lexer(&data->engine); + lexer.setCode(*code, /*line = */ 1); + + QQmlJS::Parser parser(&data->engine); + + if (! parser.parse() || !_errors.isEmpty()) { + + // Extract errors from the parser + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + + if (m.isWarning()) + continue; + + QQmlError error; + error.setUrl(url); + error.setDescription(m.message); + error.setLine(m.loc.startLine); + error.setColumn(m.loc.startColumn); + _errors << error; + + } + } + + if (_errors.isEmpty()) { + ProcessAST process(this); + process(*code, parser.ast()); + + // Set the url for process errors + for(int ii = 0; ii < _errors.count(); ++ii) + _errors[ii].setUrl(url); + } + + return _errors.isEmpty(); +} + +QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const +{ + return _refTypes; +} + +QQmlScript::Object *QQmlScript::Parser::tree() const +{ + return root; +} + +QList<QQmlScript::Import> QQmlScript::Parser::imports() const +{ + return _imports; +} + +QList<QQmlError> QQmlScript::Parser::errors() const +{ + return _errors; +} + +static void replaceWithSpace(QString &str, int idx, int n) +{ + QChar *data = str.data() + idx; + const QChar space(QLatin1Char(' ')); + for (int ii = 0; ii < n; ++ii) + *data++ = space; +} + +static QQmlScript::LocationSpan +locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset) +{ + QQmlScript::LocationSpan l; + + l.start.line = startLine; l.start.column = startColumn; + l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn(); + l.range.offset = startOffset; + l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset; + + return l; +} + +/* +Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas +are: + library +*/ +QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script) +{ + QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None; + + const QString pragma(QLatin1String("pragma")); + const QString library(QLatin1String("library")); + + QQmlJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QQmlJSGrammar::T_DOT) + return rv; + + int startOffset = l.tokenOffset(); + int startLine = l.tokenStartLine(); + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine || + script.mid(l.tokenOffset(), l.tokenLength()) != pragma) + return rv; + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine) + return rv; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return rv; + + if (pragmaValue == library) { + rv |= QQmlScript::Object::ScriptBlock::Shared; + replaceWithSpace(script, startOffset, endOffset - startOffset); + } else { + return rv; + } + } + return rv; +} + +#define CHECK_LINE if (l.tokenStartLine() != startLine) return rv; +#define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv; + +static const int uriTokens[] = { + QQmlJSGrammar::T_IDENTIFIER, + QQmlJSGrammar::T_PROPERTY, + QQmlJSGrammar::T_SIGNAL, + QQmlJSGrammar::T_READONLY, + QQmlJSGrammar::T_ON, + QQmlJSGrammar::T_BREAK, + QQmlJSGrammar::T_CASE, + QQmlJSGrammar::T_CATCH, + QQmlJSGrammar::T_CONTINUE, + QQmlJSGrammar::T_DEFAULT, + QQmlJSGrammar::T_DELETE, + QQmlJSGrammar::T_DO, + QQmlJSGrammar::T_ELSE, + QQmlJSGrammar::T_FALSE, + QQmlJSGrammar::T_FINALLY, + QQmlJSGrammar::T_FOR, + QQmlJSGrammar::T_FUNCTION, + QQmlJSGrammar::T_IF, + QQmlJSGrammar::T_IN, + QQmlJSGrammar::T_INSTANCEOF, + QQmlJSGrammar::T_NEW, + QQmlJSGrammar::T_NULL, + QQmlJSGrammar::T_RETURN, + QQmlJSGrammar::T_SWITCH, + QQmlJSGrammar::T_THIS, + QQmlJSGrammar::T_THROW, + QQmlJSGrammar::T_TRUE, + QQmlJSGrammar::T_TRY, + QQmlJSGrammar::T_TYPEOF, + QQmlJSGrammar::T_VAR, + QQmlJSGrammar::T_VOID, + QQmlJSGrammar::T_WHILE, + QQmlJSGrammar::T_CONST, + QQmlJSGrammar::T_DEBUGGER, + QQmlJSGrammar::T_RESERVED_WORD, + QQmlJSGrammar::T_WITH, + + QQmlJSGrammar::EOF_SYMBOL +}; +static inline bool isUriToken(int token) +{ + const int *current = uriTokens; + while (*current != QQmlJSGrammar::EOF_SYMBOL) { + if (*current == token) + return true; + ++current; + } + return false; +} + +QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script) +{ + JavaScriptMetaData rv; + + QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas; + + const QString pragma(QLatin1String("pragma")); + const QString js(QLatin1String(".js")); + const QString library(QLatin1String("library")); + + QQmlJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QQmlJSGrammar::T_DOT) + return rv; + + int startOffset = l.tokenOffset(); + int startLine = l.tokenStartLine(); + int startColumn = l.tokenStartColumn(); + + token = l.lex(); + + CHECK_LINE; + + if (token == QQmlJSGrammar::T_IMPORT) { + + // .import <URI> <Version> as <Identifier> + // .import <file.js> as <Identifier> + + token = l.lex(); + + CHECK_LINE; + + if (token == QQmlJSGrammar::T_STRING_LITERAL) { + + QString file = l.tokenText(); + + if (!file.endsWith(js)) + return rv; + + token = l.lex(); + + CHECK_TOKEN(T_AS); + CHECK_LINE; + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + int endOffset = l.tokenLength() + l.tokenOffset(); + + QString importId = script.mid(l.tokenOffset(), l.tokenLength()); + + if (!importId.at(0).isUpper()) + return rv; + + QQmlScript::LocationSpan location = + locationFromLexer(l, startLine, startColumn, startOffset); + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return rv; + + replaceWithSpace(script, startOffset, endOffset - startOffset); + + Import import; + import.type = Import::Script; + import.uri = file; + import.qualifier = importId; + import.location = location; + + rv.imports << import; + } else { + // URI + QString uri; + QString version; + + while (true) { + if (!isUriToken(token)) + return rv; + + uri.append(l.tokenText()); + + token = l.lex(); + CHECK_LINE; + if (token != QQmlJSGrammar::T_DOT) + break; + + uri.append(QLatin1Char('.')); + + token = l.lex(); + CHECK_LINE; + } + + CHECK_TOKEN(T_NUMERIC_LITERAL); + version = script.mid(l.tokenOffset(), l.tokenLength()); + + token = l.lex(); + + CHECK_TOKEN(T_AS); + CHECK_LINE; + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + int endOffset = l.tokenLength() + l.tokenOffset(); + + QString importId = script.mid(l.tokenOffset(), l.tokenLength()); + + if (!importId.at(0).isUpper()) + return rv; + + QQmlScript::LocationSpan location = + locationFromLexer(l, startLine, startColumn, startOffset); + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return rv; + + replaceWithSpace(script, startOffset, endOffset - startOffset); + + Import import; + import.type = Import::Library; + import.uri = uri; + import.version = version; + import.qualifier = importId; + import.location = location; + + rv.imports << import; + } + + } else if (token == QQmlJSGrammar::T_IDENTIFIER && + script.mid(l.tokenOffset(), l.tokenLength()) == pragma) { + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + if (pragmaValue == library) { + pragmas |= QQmlScript::Object::ScriptBlock::Shared; + replaceWithSpace(script, startOffset, endOffset - startOffset); + } else { + return rv; + } + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return rv; + + } else { + return rv; + } + } + return rv; +} + +void QQmlScript::Parser::clear() +{ + _imports.clear(); + qDeleteAll(_refTypes); + _refTypes.clear(); + _errors.clear(); + + if (data) { + delete data; + data = 0; + } + + _pool.clear(); +} + +QQmlScript::TypeReference *QQmlScript::Parser::findOrCreateType(const QString &name) +{ + TypeReference *type = 0; + int i = 0; + for (; i < _refTypes.size(); ++i) { + if (_refTypes.at(i)->name == name) { + type = _refTypes.at(i); + break; + } + } + if (!type) { + type = new TypeReference(i, name); + _refTypes.append(type); + } + + return type; +} + +void QQmlScript::Parser::setTree(QQmlScript::Object *tree) +{ + Q_ASSERT(! root); + + root = tree; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscript_p.h b/src/qml/qml/qqmlscript_p.h new file mode 100644 index 0000000000..ddf4c9a392 --- /dev/null +++ b/src/qml/qml/qqmlscript_p.h @@ -0,0 +1,533 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLSCRIPT_P_H +#define QQMLSCRIPT_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/qqmlerror.h> + +#include <private/qfieldlist_p.h> +#include <private/qhashfield_p.h> +#include <private/qfastmetabuilder_p.h> +#include <private/qqmlpool_p.h> +#include <private/qqmlpropertycache_p.h> + +#include <QtCore/QList> +#include <QtCore/QUrl> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QByteArray; +class QQmlPropertyCache; +namespace QQmlJS { namespace AST { class Node; class StringLiteral; } } +namespace QQmlCompilerTypes { struct BindingReference; struct ComponentCompileState; } + +namespace QQmlScript { + +struct Location +{ + Location() : line(-1), column(-1) {} + int line; + int column; + + inline bool operator<(const Location &other) { + return line < other.line || + (line == other.line && column < other.column); + } +}; + +struct LocationRange +{ + LocationRange() : offset(0), length(0) {} + quint32 offset; + quint32 length; +}; + +struct LocationSpan +{ + Location start; + Location end; + LocationRange range; + + bool operator<(LocationSpan &o) const { + return (start.line < o.start.line) || + (start.line == o.start.line && start.column < o.start.column); + } +}; + +class Import +{ +public: + Import() : type(Library) {} + + enum Type { Library, File, Script }; + Type type; + + QString uri; + QString qualifier; + QString version; + + void extractVersion(int *maj, int *min) const; + + QQmlScript::LocationSpan location; +}; + +class Object; +class TypeReference +{ +public: + TypeReference(int typeId, const QString &typeName) : id(typeId), name(typeName) {} + + int id; + // type as it has been referenced in Qml + QString name; + // objects in parse tree referencing the type + QList<QQmlScript::Object*> refObjects; +}; + +class Object; +class Property; + +class Q_QML_EXPORT Variant +{ +public: + enum Type { + Invalid, + Boolean, + Number, + String, + Script + }; + + Variant(); + Variant(const Variant &); + explicit Variant(bool); + explicit Variant(double, const QStringRef &asWritten = QStringRef()); + explicit Variant(QQmlJS::AST::StringLiteral *); + explicit Variant(const QStringRef &asWritten, QQmlJS::AST::Node *); + Variant &operator=(const Variant &); + + Type type() const; + + bool isBoolean() const { return type() == Boolean; } + bool isNumber() const { return type() == Number; } + bool isString() const { return type() == String; } + bool isScript() const { return type() == Script; } + bool isStringList() const; + + bool asBoolean() const; + QString asString() const; + double asNumber() const; + QString asScript() const; + QQmlJS::AST::Node *asAST() const; + QStringList asStringList() const; + +private: + Type t; + union { + bool b; + double d; + QQmlJS::AST::StringLiteral *l; + QQmlJS::AST::Node *n; + }; + QStringRef asWritten; +}; + +class Value : public QQmlPool::POD +{ +public: + Value(); + + enum Type { + // The type of this value assignment is not yet known + Unknown, + // This is used as a literal property assignment + Literal, + // This is used as a property binding assignment + PropertyBinding, + // This is used as a QQmlPropertyValueSource assignment + ValueSource, + // This is used as a QQmlPropertyValueInterceptor assignment + ValueInterceptor, + // This is used as a property QObject assignment + CreatedObject, + // This is used as a signal object assignment + SignalObject, + // This is used as a signal expression assignment + SignalExpression, + // This is used as an id assignment only + Id + }; + Type type; + + // ### Temporary (for id only) + QString primitive() const { return value.isString() ? value.asString() : value.asScript(); } + + // Primitive value + Variant value; + // Object value + Object *object; + + LocationSpan location; + + // Used by compiler + union { + QQmlCompilerTypes::BindingReference *bindingReference; + int signalExpressionContextStack; + }; + + // Used in Property::ValueList lists + Value *nextValue; +}; + +class Property : public QQmlPool::POD +{ +public: + Property(); + + // The Object to which this property is attached + Object *parent; + + Object *getValue(const LocationSpan &); + void addValue(Value *v); + void addOnValue(Value *v); + + // The QVariant::Type of the property, or 0 (QVariant::Invalid) if + // unknown. + int type; + // The metaobject index of this property, or -1 if unknown. + int index; + // The core data in the case of a regular property. + // XXX This has to be a value now as the synthCache may change during + // compilation which invalidates pointers. We should fix this. + QQmlPropertyData core; + + // Returns true if this is an empty property - both value and values + // are unset. + bool isEmpty() const; + + typedef QFieldList<Value, &Value::nextValue> ValueList; + // The list of values assigned to this property. Content in values + // and value are mutually exclusive + ValueList values; + // The list of values assigned to this property using the "on" syntax + ValueList onValues; + // The accessed property. This is used to represent dot properties. + // Content in value and values are mutually exclusive. + Object *value; + // The property name + const QHashedStringRef &name() const { return _name; } + void setName(const QString &n) { _name = QHashedStringRef(pool()->NewString(n)); } + void setName(const QHashedStringRef &n) { _name = n; } + // True if this property was accessed as the default property. + bool isDefault; + // True if the setting of this property will be deferred. Set by the + // QQmlCompiler + bool isDeferred; + // True if this property is a value-type pseudo-property + bool isValueTypeSubProperty; + // True if this property is a property alias. Set by the + // QQmlCompiler + bool isAlias; + // True if this is a readonly property declaration + bool isReadOnlyDeclaration; + + // Used for scriptStringProperties + int scriptStringScope; + + LocationSpan location; + LocationRange listValueRange; + + // Used in Object::MainPropertyList + Property *nextMainProperty; + + // Used in Object::PropertyList lists + Property *nextProperty; + +private: + friend class Object; + QHashedStringRef _name; +}; + +class Object : public QQmlPool::Class +{ +public: + Object(); + virtual ~Object(); + + // Type of the object. The integer is an index into the + // QQmlCompiledData::types array, or -1 if the object is a property + // group. + int type; + + // The fully-qualified name of this type + QString typeName; + // The id assigned to the object (if any). Set by the QQmlCompiler + QString id; + // The id index assigned to the object (if any). Set by the QQmlCompiler + int idIndex; + // Custom parsed data + QByteArray custom; + // Bit mask of the properties assigned bindings + QByteArray bindingBitmask; + void setBindingBit(int); + // Returns the metaobject for this type, or 0 if not available. + // Internally selectd between the metatype and extObject variables + const QMetaObject *metaObject() const; + + // The compile time metaobject for this type + const QMetaObject *metatype; + // The synthesized metaobject, if QML added signals or properties to + // this type. Otherwise null + QAbstractDynamicMetaObject extObject; + QByteArray metadata; // Generated by compiler + QByteArray synthdata; // Generated by compiler + QQmlPropertyCache *synthCache; // Generated by compiler + + Property *getDefaultProperty(); + // name ptr must be guarenteed to remain valid + Property *getProperty(const QHashedStringRef &name, bool create=true); + Property *getProperty(const QStringRef &name, bool create=true); + Property *getProperty(const QString &name, bool create=true); + + Property *defaultProperty; + + typedef QFieldList<Property, &Property::nextMainProperty> MainPropertyList; + MainPropertyList properties; + QHashField propertiesHashField; + + // Output of the compilation phase (these properties continue to exist + // in either the defaultProperty or properties members too) + void addValueProperty(Property *); + void addSignalProperty(Property *); + void addAttachedProperty(Property *); + void addGroupedProperty(Property *); + void addValueTypeProperty(Property *); + void addScriptStringProperty(Property *); + + typedef QFieldList<Property, &Property::nextProperty> PropertyList; + PropertyList valueProperties; + PropertyList signalProperties; + PropertyList attachedProperties; + PropertyList groupedProperties; + PropertyList valueTypeProperties; + PropertyList scriptStringProperties; + + // Script blocks that were nested under this object + struct ScriptBlock { + enum Pragma { + None = 0x00000000, + Shared = 0x00000001 + }; + Q_DECLARE_FLAGS(Pragmas, Pragma) + + QString code; + QString file; + Pragmas pragmas; + }; + + // The bytes to cast instances by to get to the QQmlParserStatus + // interface. -1 indicates the type doesn't support this interface. + // Set by the QQmlCompiler. + int parserStatusCast; + + LocationSpan location; + + struct DynamicProperty : public QQmlPool::POD + { + DynamicProperty(); + + enum Type { Var, Variant, Int, Bool, Real, String, Url, Color, + Time, Date, DateTime, Alias, Custom, CustomList }; + + quint32 isDefaultProperty:1; + quint32 isReadOnly:1; + + Type type; + + QHashedStringRef customType; + QHashedStringRef name; + QQmlScript::Property *defaultValue; + LocationSpan location; + Location nameLocation; + + // Used by Object::DynamicPropertyList + DynamicProperty *nextProperty; + + // Used by the compiler + QByteArray *resolvedCustomTypeName; + QFastMetaBuilder::StringRef typeRef; + QFastMetaBuilder::StringRef nameRef; + QFastMetaBuilder::StringRef changedSignatureRef; + }; + + struct DynamicSignal : public QQmlPool::POD + { + DynamicSignal(); + + QHashedStringRef name; + QQmlPool::List<QHashedCStringRef> parameterTypes; + QQmlPool::List<QHashedStringRef> parameterNames; + + int parameterTypesLength() const; + int parameterNamesLength() const; + + // Used by Object::DynamicSignalList + DynamicSignal *nextSignal; + + // Used by the compiler + QFastMetaBuilder::StringRef signatureRef; + QFastMetaBuilder::StringRef parameterNamesRef; + LocationSpan location; + }; + + struct DynamicSlot : public QQmlPool::Class + { + DynamicSlot(); + + QHashedStringRef name; + QString body; + QList<QByteArray> parameterNames; + LocationSpan location; + + int parameterNamesLength() const; + + // Used by Object::DynamicSlotList + DynamicSlot *nextSlot; + + // Used by the compiler + QFastMetaBuilder::StringRef signatureRef; + QFastMetaBuilder::StringRef parameterNamesRef; + }; + + // The list of dynamic properties + typedef QFieldList<DynamicProperty, &DynamicProperty::nextProperty> DynamicPropertyList; + DynamicPropertyList dynamicProperties; + // The list of dynamic signals + typedef QFieldList<DynamicSignal, &DynamicSignal::nextSignal> DynamicSignalList; + DynamicSignalList dynamicSignals; + // The list of dynamic slots + typedef QFieldList<DynamicSlot, &DynamicSlot::nextSlot> DynamicSlotList; + DynamicSlotList dynamicSlots; + + // Used by compiler + QQmlCompilerTypes::ComponentCompileState *componentCompileState; + + // Used by ComponentCompileState::AliasingObjectsList + Object *nextAliasingObject; + // Used by ComponentComppileState::IdList + Object *nextIdObject; +}; + +class ParserJsASTData; +class Q_AUTOTEST_EXPORT Parser +{ +public: + Parser(); + ~Parser(); + + bool parse(const QByteArray &data, const QUrl &url = QUrl(), + const QString &urlString = QString()); + + QList<TypeReference*> referencedTypes() const; + + QQmlScript::Object *tree() const; + QList<Import> imports() const; + + void clear(); + + QList<QQmlError> errors() const; + + class JavaScriptMetaData { + public: + JavaScriptMetaData() + : pragmas(QQmlScript::Object::ScriptBlock::None) {} + + QQmlScript::Object::ScriptBlock::Pragmas pragmas; + QList<Import> imports; + }; + + static QQmlScript::Object::ScriptBlock::Pragmas extractPragmas(QString &); + static JavaScriptMetaData extractMetaData(QString &); + + +// ### private: + TypeReference *findOrCreateType(const QString &name); + void setTree(QQmlScript::Object *tree); + + void setScriptFile(const QString &filename) {_scriptFile = filename; } + QString scriptFile() const { return _scriptFile; } + +// ### private: + QList<QQmlError> _errors; + + QQmlPool _pool; + QQmlScript::Object *root; + QList<Import> _imports; + QList<TypeReference*> _refTypes; + QString _scriptFile; + ParserJsASTData *data; +}; + +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlScript::Object::ScriptBlock::Pragmas); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlScript::Variant) + +QT_END_HEADER + +#endif // QQMLSCRIPT_P_H diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp new file mode 100644 index 0000000000..ed7a6affa1 --- /dev/null +++ b/src/qml/qml/qqmlscriptstring.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlscriptstring.h" +#include "qqmlscriptstring_p.h" + +QT_BEGIN_NAMESPACE + +/*! +\class QQmlScriptString +\since 4.7 +\brief The QQmlScriptString class encapsulates a script and its context. + +QQmlScriptString is used to create QObject properties that accept a script "assignment" from QML. + +Normally, the following QML would result in a binding being established for the \c script +property; i.e. \c script would be assigned the value obtained from running \c {myObj.value = Math.max(myValue, 100)} + +\qml +MyType { + script: myObj.value = Math.max(myValue, 100) +} +\endqml + +If instead the property had a type of QQmlScriptString, +the script itself -- \e {myObj.value = Math.max(myValue, 100)} -- would be passed to the \c script property +and the class could choose how to handle it. Typically, the class will evaluate +the script at some later time using a QQmlExpression. + +\code +QQmlExpression expr(scriptString); +expr.evaluate(); +\endcode + +\sa QQmlExpression +*/ + +/*! +Constructs an empty instance. +*/ +QQmlScriptString::QQmlScriptString() +: d(new QQmlScriptStringPrivate) +{ +} + +/*! +Copies \a other. +*/ +QQmlScriptString::QQmlScriptString(const QQmlScriptString &other) +: d(other.d) +{ +} + +/*! +\internal +*/ +QQmlScriptString::~QQmlScriptString() +{ +} + +/*! +Assigns \a other to this. +*/ +QQmlScriptString &QQmlScriptString::operator=(const QQmlScriptString &other) +{ + d = other.d; + return *this; +} + +/*! +Returns the context for the script. +*/ +QQmlContext *QQmlScriptString::context() const +{ + return d->context; +} + +/*! +Sets the \a context for the script. +*/ +void QQmlScriptString::setContext(QQmlContext *context) +{ + d->context = context; +} + +/*! +Returns the scope object for the script. +*/ +QObject *QQmlScriptString::scopeObject() const +{ + return d->scope; +} + +/*! +Sets the scope \a object for the script. +*/ +void QQmlScriptString::setScopeObject(QObject *object) +{ + d->scope = object; +} + +/*! +Returns the script text. +*/ +QString QQmlScriptString::script() const +{ + return d->script; +} + +/*! +Sets the \a script text. +*/ +void QQmlScriptString::setScript(const QString &script) +{ + d->script = script; +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h new file mode 100644 index 0000000000..15db9088f7 --- /dev/null +++ b/src/qml/qml/qqmlscriptstring.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTSTRING_H +#define QQMLSCRIPTSTRING_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/qstring.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QObject; +class QQmlContext; +class QQmlScriptStringPrivate; +class Q_QML_EXPORT QQmlScriptString +{ +public: + QQmlScriptString(); + QQmlScriptString(const QQmlScriptString &); + ~QQmlScriptString(); + + QQmlScriptString &operator=(const QQmlScriptString &); + + QQmlContext *context() const; + void setContext(QQmlContext *); + + QObject *scopeObject() const; + void setScopeObject(QObject *); + + QString script() const; + void setScript(const QString &); + +private: + QSharedDataPointer<QQmlScriptStringPrivate> d; + + friend class QQmlVME; + friend class QQmlExpression; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlScriptString) + +QT_END_HEADER + +#endif // QQMLSCRIPTSTRING_H + diff --git a/src/qml/qml/qqmlscriptstring_p.h b/src/qml/qml/qqmlscriptstring_p.h new file mode 100644 index 0000000000..15786c7aae --- /dev/null +++ b/src/qml/qml/qqmlscriptstring_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTSTRING_P_H +#define QQMLSCRIPTSTRING_P_H + +#include <QtQml/qqmlcontext.h> + +QT_BEGIN_NAMESPACE + +class QQmlScriptStringPrivate : public QSharedData +{ +public: + QQmlScriptStringPrivate() : context(0), scope(0), bindingId(-1), lineNumber(-1), columnNumber(-1) {} + + QQmlContext *context; + QObject *scope; + QString script; + int bindingId; + int lineNumber; + int columnNumber; +}; + +QT_END_NAMESPACE + +#endif // QQMLSCRIPTSTRING_P_H diff --git a/src/qml/qml/qqmlstringconverters.cpp b/src/qml/qml/qqmlstringconverters.cpp new file mode 100644 index 0000000000..2c7f6c9f6e --- /dev/null +++ b/src/qml/qml/qqmlstringconverters.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlstringconverters_p.h" + +#include <QtGui/qcolor.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtCore/qvariant.h> +#include <QtCore/qdatetime.h> + +QT_BEGIN_NAMESPACE + +static uchar fromHex(const uchar c, const uchar c2) +{ + uchar rv = 0; + if (c >= '0' && c <= '9') + rv += (c - '0') * 16; + else if (c >= 'A' && c <= 'F') + rv += (c - 'A' + 10) * 16; + else if (c >= 'a' && c <= 'f') + rv += (c - 'a' + 10) * 16; + + if (c2 >= '0' && c2 <= '9') + rv += (c2 - '0'); + else if (c2 >= 'A' && c2 <= 'F') + rv += (c2 - 'A' + 10); + else if (c2 >= 'a' && c2 <= 'f') + rv += (c2 - 'a' + 10); + + return rv; +} + +static uchar fromHex(const QString &s, int idx) +{ + uchar c = s.at(idx).toAscii(); + uchar c2 = s.at(idx + 1).toAscii(); + return fromHex(c, c2); +} + +QVariant QQmlStringConverters::variantFromString(const QString &s) +{ + if (s.isEmpty()) + return QVariant(s); + bool ok = false; + QRectF r = rectFFromString(s, &ok); + if (ok) return QVariant(r); + QColor c = colorFromString(s, &ok); + if (ok) return QVariant(c); + QPointF p = pointFFromString(s, &ok); + if (ok) return QVariant(p); + QSizeF sz = sizeFFromString(s, &ok); + if (ok) return QVariant(sz); + QVector3D v = vector3DFromString(s, &ok); + if (ok) return QVariant::fromValue(v); + QVector4D v4 = vector4DFromString(s, &ok); + if (ok) return QVariant::fromValue(v4); + + return QVariant(s); +} + +QVariant QQmlStringConverters::variantFromString(const QString &s, int preferredType, bool *ok) +{ + switch (preferredType) { + case QMetaType::Int: + return QVariant(int(qRound(s.toDouble(ok)))); + case QMetaType::UInt: + return QVariant(uint(qRound(s.toDouble(ok)))); + case QMetaType::QColor: + return QVariant::fromValue(colorFromString(s, ok)); +#ifndef QT_NO_DATESTRING + case QMetaType::QDate: + return QVariant::fromValue(dateFromString(s, ok)); + case QMetaType::QTime: + return QVariant::fromValue(timeFromString(s, ok)); + case QMetaType::QDateTime: + return QVariant::fromValue(dateTimeFromString(s, ok)); +#endif // QT_NO_DATESTRING + case QMetaType::QPointF: + return QVariant::fromValue(pointFFromString(s, ok)); + case QMetaType::QPoint: + return QVariant::fromValue(pointFFromString(s, ok).toPoint()); + case QMetaType::QSizeF: + return QVariant::fromValue(sizeFFromString(s, ok)); + case QMetaType::QSize: + return QVariant::fromValue(sizeFFromString(s, ok).toSize()); + case QMetaType::QRectF: + return QVariant::fromValue(rectFFromString(s, ok)); + case QMetaType::QRect: + return QVariant::fromValue(rectFFromString(s, ok).toRect()); + case QMetaType::QVector3D: + return QVariant::fromValue(vector3DFromString(s, ok)); + case QMetaType::QVector4D: + return QVariant::fromValue(vector4DFromString(s, ok)); + default: + if (ok) *ok = false; + return QVariant(); + } +} + +QColor QQmlStringConverters::colorFromString(const QString &s, bool *ok) +{ + if (s.length() == 9 && s.startsWith(QLatin1Char('#'))) { + uchar a = fromHex(s, 1); + uchar r = fromHex(s, 3); + uchar g = fromHex(s, 5); + uchar b = fromHex(s, 7); + if (ok) *ok = true; + return QColor(r, g, b, a); + } else { + QColor rv(s); + if (ok) *ok = rv.isValid(); + return rv; + } +} + +#ifndef QT_NO_DATESTRING +QDate QQmlStringConverters::dateFromString(const QString &s, bool *ok) +{ + QDate d = QDate::fromString(s, Qt::ISODate); + if (ok) *ok = d.isValid(); + return d; +} + +QTime QQmlStringConverters::timeFromString(const QString &s, bool *ok) +{ + QTime t = QTime::fromString(s, Qt::ISODate); + if (ok) *ok = t.isValid(); + return t; +} + +QDateTime QQmlStringConverters::dateTimeFromString(const QString &s, bool *ok) +{ + QDateTime d = QDateTime::fromString(s, Qt::ISODate); + if (ok) *ok = d.isValid(); + return d; +} +#endif // QT_NO_DATESTRING + +//expects input of "x,y" +QPointF QQmlStringConverters::pointFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 1) { + if (ok) + *ok = false; + return QPointF(); + } + + bool xGood, yGood; + int index = s.indexOf(QLatin1Char(',')); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1).toDouble(&yGood); + if (!xGood || !yGood) { + if (ok) + *ok = false; + return QPointF(); + } + + if (ok) + *ok = true; + return QPointF(xCoord, yCoord); +} + +//expects input of "widthxheight" +QSizeF QQmlStringConverters::sizeFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QSizeF(); + } + + bool wGood, hGood; + int index = s.indexOf(QLatin1Char('x')); + qreal width = s.left(index).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!wGood || !hGood) { + if (ok) + *ok = false; + return QSizeF(); + } + + if (ok) + *ok = true; + return QSizeF(width, height); +} + +//expects input of "x,y,widthxheight" //### use space instead of second comma? +QRectF QQmlStringConverters::rectFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 2 || s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QRectF(); + } + + bool xGood, yGood, wGood, hGood; + int index = s.indexOf(QLatin1Char(',')); + qreal x = s.left(index).toDouble(&xGood); + int index2 = s.indexOf(QLatin1Char(','), index+1); + qreal y = s.mid(index+1, index2-index-1).toDouble(&yGood); + index = s.indexOf(QLatin1Char('x'), index2+1); + qreal width = s.mid(index2+1, index-index2-1).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!xGood || !yGood || !wGood || !hGood) { + if (ok) + *ok = false; + return QRectF(); + } + + if (ok) + *ok = true; + return QRectF(x, y, width, height); +} + +//expects input of "x,y,z" +QVector3D QQmlStringConverters::vector3DFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 2) { + if (ok) + *ok = false; + return QVector3D(); + } + + bool xGood, yGood, zGood; + int index = s.indexOf(QLatin1Char(',')); + int index2 = s.indexOf(QLatin1Char(','), index+1); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1, index2-index-1).toDouble(&yGood); + qreal zCoord = s.mid(index2+1).toDouble(&zGood); + if (!xGood || !yGood || !zGood) { + if (ok) + *ok = false; + return QVector3D(); + } + + if (ok) + *ok = true; + return QVector3D(xCoord, yCoord, zCoord); +} + +//expects input of "x,y,z,w" +QVector4D QQmlStringConverters::vector4DFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 3) { + if (ok) + *ok = false; + return QVector4D(); + } + + bool xGood, yGood, zGood, wGood; + int index = s.indexOf(QLatin1Char(',')); + int index2 = s.indexOf(QLatin1Char(','), index+1); + int index3 = s.indexOf(QLatin1Char(','), index2+1); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1, index2-index-1).toDouble(&yGood); + qreal zCoord = s.mid(index2+1, index3-index2-1).toDouble(&zGood); + qreal wCoord = s.mid(index3+1).toDouble(&wGood); + if (!xGood || !yGood || !zGood || !wGood) { + if (ok) + *ok = false; + return QVector4D(); + } + + if (ok) + *ok = true; + return QVector4D(xCoord, yCoord, zCoord, wCoord); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlstringconverters_p.h b/src/qml/qml/qqmlstringconverters_p.h new file mode 100644 index 0000000000..8f6fb2485d --- /dev/null +++ b/src/qml/qml/qqmlstringconverters_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSTRINGCONVERTERS_P_H +#define QQMLSTRINGCONVERTERS_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 <private/qtqmlglobal_p.h> + +QT_BEGIN_NAMESPACE + +class QColor; +class QPointF; +class QSizeF; +class QRectF; +class QString; +class QByteArray; +class QVector3D; +class QVector4D; + +// XXX - Bauhaus currently uses these methods which is why they're exported +namespace QQmlStringConverters +{ + QVariant Q_QML_PRIVATE_EXPORT variantFromString(const QString &); + QVariant Q_QML_PRIVATE_EXPORT variantFromString(const QString &, int preferredType, bool *ok = 0); + + QColor Q_QML_PRIVATE_EXPORT colorFromString(const QString &, bool *ok = 0); +#ifndef QT_NO_DATESTRING + QDate Q_QML_PRIVATE_EXPORT dateFromString(const QString &, bool *ok = 0); + QTime Q_QML_PRIVATE_EXPORT timeFromString(const QString &, bool *ok = 0); + QDateTime Q_QML_PRIVATE_EXPORT dateTimeFromString(const QString &, bool *ok = 0); +#endif + QPointF Q_QML_PRIVATE_EXPORT pointFFromString(const QString &, bool *ok = 0); + QSizeF Q_QML_PRIVATE_EXPORT sizeFFromString(const QString &, bool *ok = 0); + QRectF Q_QML_PRIVATE_EXPORT rectFFromString(const QString &, bool *ok = 0); + QVector3D Q_QML_PRIVATE_EXPORT vector3DFromString(const QString &, bool *ok = 0); + QVector4D Q_QML_PRIVATE_EXPORT vector4DFromString(const QString &, bool *ok = 0); +} + +QT_END_NAMESPACE + +#endif // QQMLSTRINGCONVERTERS_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp new file mode 100644 index 0000000000..cde2885f0c --- /dev/null +++ b/src/qml/qml/qqmltypeloader.cpp @@ -0,0 +1,1928 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypeloader_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmlthread_p.h> +#include <private/qqmlcompiler_p.h> +#include <private/qqmlcomponent_p.h> +#include <private/qqmlprofilerservice_p.h> + +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmutex.h> +#include <QtCore/qthread.h> +#include <QtCore/qdiriterator.h> +#include <QtCore/qwaitcondition.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlextensioninterface.h> + +#if defined (Q_OS_UNIX) +#include <sys/types.h> +#include <dirent.h> +#endif + +// #define DATABLOB_DEBUG + +#ifdef DATABLOB_DEBUG + +#define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in main thread"); } while(false) +#define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in load thread"); } while(false) +#define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->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 + +QT_BEGIN_NAMESPACE + +// 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 QQmlDataLoader 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 QQmlDataLoaderNetworkReplyProxy : public QObject +{ + Q_OBJECT +public: + QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l); + +public slots: + void finished(); + void downloadProgress(qint64, qint64); + +private: + QQmlDataLoader *l; +}; + +class QQmlDataLoaderThread : public QQmlThread +{ + typedef QQmlDataLoaderThread This; + +public: + QQmlDataLoaderThread(QQmlDataLoader *loader); + QNetworkAccessManager *networkAccessManager() const; + QQmlDataLoaderNetworkReplyProxy *networkReplyProxy() const; + + void load(QQmlDataBlob *b); + void loadAsync(QQmlDataBlob *b); + void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); + void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &); + void callCompleted(QQmlDataBlob *b); + void callDownloadProgressChanged(QQmlDataBlob *b, qreal p); + void initializeEngine(QQmlExtensionInterface *, const char *); + +protected: + virtual void shutdownThread(); + +private: + void loadThread(QQmlDataBlob *b); + void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &); + void callCompletedMain(QQmlDataBlob *b); + void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p); + void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); + + QQmlDataLoader *m_loader; + mutable QNetworkAccessManager *m_networkAccessManager; + mutable QQmlDataLoaderNetworkReplyProxy *m_networkReplyProxy; +}; + + +QQmlDataLoaderNetworkReplyProxy::QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l) +: l(l) +{ +} + +void QQmlDataLoaderNetworkReplyProxy::finished() +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + l->networkReplyFinished(reply); +} + +void QQmlDataLoaderNetworkReplyProxy::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); +} + +/* +Returns the set of QML files in path (qmldir, *.qml, *.js). The caller +is responsible for deleting the returned data. +Returns 0 if the directory does not exist. +*/ +#if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN) +static QStringHash<bool> *qmlFilesInDirectory(const QString &path) +{ + QByteArray name(QFile::encodeName(path)); + DIR *dd = opendir(name); + if (!dd) + return 0; + + struct dirent *result; + union { + struct dirent d; + char b[offsetof (struct dirent, d_name) + NAME_MAX + 1]; + } u; + + QStringHash<bool> *files = new QStringHash<bool>; + while (readdir_r(dd, &u.d, &result) == 0 && result != 0) { + if (!strcmp(u.d.d_name, "qmldir")) { + files->insert(QLatin1String("qmldir"), true); + continue; + } + int len = strlen(u.d.d_name); + if (len < 4) + continue; + if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js")) + files->insert(QFile::decodeName(u.d.d_name), true); +#if defined(Q_OS_DARWIN) + else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so") + || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle"))) + files->insert(QFile::decodeName(u.d.d_name), true); +#else // Unix + else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl")) + files->insert(QFile::decodeName(u.d.d_name), true); +#endif + } + + closedir(dd); + return files; +} +#else +static QStringHash<bool> *qmlFilesInDirectory(const QString &path) +{ + QDirIterator dir(path, QDir::Files); + if (!dir.hasNext()) + return 0; + QStringHash<bool> *files = new QStringHash<bool>; + while (dir.hasNext()) { + dir.next(); + QString fileName = dir.fileName(); + if (fileName == QLatin1String("qmldir") + || fileName.endsWith(QLatin1String(".qml")) + || fileName.endsWith(QLatin1String(".js")) +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + || fileName.endsWith(QLatin1String(".dll")) +#elif defined(Q_OS_DARWIN) + || fileName.endsWith(QLatin1String(".dylib")) + || fileName.endsWith(QLatin1String(".so")) + || fileName.endsWith(QLatin1String(".bundle")) +#else // Unix + || fileName.endsWith(QLatin1String(".so")) + || fileName.endsWith(QLatin1String(".sl")) +#endif + ) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + fileName = fileName.toLower(); +#endif + files->insert(fileName, true); + } + } + return files; +} +#endif + + +/*! +\class QQmlDataBlob +\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlDataLoader. +\internal + +QQmlDataBlob's are loaded by a QQmlDataLoader. The user creates the QQmlDataBlob +and then calls QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() to load it. +The QQmlDataLoader invokes callbacks on the QQmlDataBlob as data becomes available. +*/ + +/*! +\enum QQmlDataBlob::Status + +This enum describes the status of the data blob. + +\list +\o Null The blob has not yet been loaded by a QQmlDataLoader +\o Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been +invoked or has not yet returned. +\o 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. +\o Complete The blob's data has been loaded and all dependencies are done. +\o Error An error has been set on this blob. +\endlist +*/ + +/*! +\enum QQmlDataBlob::Type + +This enum describes the type of the data blob. + +\list +\o QmlFile This is a QQmlTypeData +\o JavaScriptFile This is a QQmlScriptData +\o 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) +: m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0), + m_inCallback(false), m_isDone(false) +{ +} + +/*! \internal */ +QQmlDataBlob::~QQmlDataBlob() +{ + Q_ASSERT(m_waitingOnMe.isEmpty()); + + cancelAllWaitingFor(); +} + +/*! +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; +} + +/*! +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 blob url passed to the constructor. If a network redirect +happens while fetching the data, this url remains the same. + +\sa finalUrl() +*/ +QUrl QQmlDataBlob::url() const +{ + return m_url; +} + +/*! +Returns the final url of the data. Initially this is the same as +url(), but if a network redirect happens while fetching the data, this url +is updated to reflect the new location. + +May only be called from the load thread, or after the blob isCompleteOrError(). +*/ +QUrl QQmlDataBlob::finalUrl() const +{ + Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread())); + return m_finalUrl; +} + +/*! +Returns the finalUrl() as a string. +*/ +QString QQmlDataBlob::finalUrlString() const +{ + Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread())); + 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_manager && m_manager->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); + + cancelAllWaitingFor(); + + if (!m_inCallback) + tryDone(); +} + +/*! +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 || + m_waitingFor.contains(blob)) + return; + + blob->addref(); + + m_data.setStatus(WaitingForDependencies); + + m_waitingFor.append(blob); + blob->m_waitingOnMe.append(this); +} + +/*! +\fn void QQmlDataBlob::dataReceived(const QByteArray &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() +{ +} + +/*! +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_finalUrl); + + const char *errorString = 0; + 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); +} + +/*! +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() +{ +} + +/*! +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(url().toString())); +#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); + if (m_data.isAsync()) { +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataBlob: Dispatching completed"); +#endif + m_manager->m_thread->callCompleted(this); + } + + release(); + } +} + +void QQmlDataBlob::cancelAllWaitingFor() +{ + while (m_waitingFor.count()) { + QQmlDataBlob *blob = m_waitingFor.takeLast(); + + Q_ASSERT(blob->m_waitingOnMe.contains(this)); + + blob->m_waitingOnMe.removeOne(this); + + blob->release(); + } +} + +void QQmlDataBlob::notifyAllWaitingOnMe() +{ + while (m_waitingOnMe.count()) { + QQmlDataBlob *blob = m_waitingOnMe.takeLast(); + + Q_ASSERT(blob->m_waitingFor.contains(this)); + + blob->notifyComplete(this); + } +} + +void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) +{ + Q_ASSERT(m_waitingFor.contains(blob)); + Q_ASSERT(blob->status() == Error || blob->status() == Complete); + + m_inCallback = true; + + if (blob->status() == Error) { + dependencyError(blob); + } else if (blob->status() == Complete) { + dependencyComplete(blob); + } + + m_waitingFor.removeOne(blob); + blob->release(); + + 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; + } +} + +QQmlDataLoaderThread::QQmlDataLoaderThread(QQmlDataLoader *loader) +: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0) +{ +} + +QNetworkAccessManager *QQmlDataLoaderThread::networkAccessManager() const +{ + Q_ASSERT(isThisThread()); + if (!m_networkAccessManager) { + m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0); + m_networkReplyProxy = new QQmlDataLoaderNetworkReplyProxy(m_loader); + } + + return m_networkAccessManager; +} + +QQmlDataLoaderNetworkReplyProxy *QQmlDataLoaderThread::networkReplyProxy() const +{ + Q_ASSERT(isThisThread()); + Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first + return m_networkReplyProxy; +} + +void QQmlDataLoaderThread::load(QQmlDataBlob *b) +{ + b->addref(); + callMethodInThread(&This::loadThread, b); +} + +void QQmlDataLoaderThread::loadAsync(QQmlDataBlob *b) +{ + b->addref(); + postMethodToThread(&This::loadThread, b); +} + +void QQmlDataLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + callMethodInThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlDataLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + postMethodToThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlDataLoaderThread::callCompleted(QQmlDataBlob *b) +{ + b->addref(); + postMethodToMain(&This::callCompletedMain, b); +} + +void QQmlDataLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) +{ + b->addref(); + postMethodToMain(&This::callDownloadProgressChangedMain, b, p); +} + +void QQmlDataLoaderThread::initializeEngine(QQmlExtensionInterface *iface, + const char *uri) +{ + callMethodInMain(&This::initializeEngineMain, iface, uri); +} + +void QQmlDataLoaderThread::shutdownThread() +{ + delete m_networkAccessManager; + m_networkAccessManager = 0; + delete m_networkReplyProxy; + m_networkReplyProxy = 0; +} + +void QQmlDataLoaderThread::loadThread(QQmlDataBlob *b) +{ + m_loader->loadThread(b); + b->release(); +} + +void QQmlDataLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d) +{ + m_loader->loadWithStaticDataThread(b, d); + b->release(); +} + +void QQmlDataLoaderThread::callCompletedMain(QQmlDataBlob *b) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataLoaderThread: %s completed() callback", qPrintable(b->url().toString())); +#endif + b->completed(); + b->release(); +} + +void QQmlDataLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataLoaderThread: %s downloadProgressChanged(%f) callback", + qPrintable(b->url().toString()), p); +#endif + b->downloadProgressChanged(p); + b->release(); +} + +void QQmlDataLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, + const char *uri) +{ + Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); + iface->initializeEngine(m_loader->engine(), uri); +} + +/*! +\class QQmlDataLoader +\brief The QQmlDataLoader class abstracts loading files and their dependencies over the network. +\internal + +The QQmlDataLoader class is provided for the exclusive use of the QQmlTypeLoader class. + +Clients create QQmlDataBlob instances and submit them to the QQmlDataLoader class +through the QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() methods. +The loader then fetches the data over the network or from the local file system in an efficient way. +QQmlDataBlob is an abstract class, so should always be specialized. + +Once data is received, the QQmlDataBlob::dataReceived() method is invoked on the blob. The +derived class should use this callback to process the received data. Processing of the data can +result in an error being set (QQmlDataBlob::setError()), or one or more dependencies being +created (QQmlDataBlob::addDependency()). Dependencies are other QQmlDataBlob's that +are required before processing can fully complete. + +To complete processing, the QQmlDataBlob::done() callback is invoked. done() is called when +one of these three preconditions are met. + +\list 1 +\o The QQmlDataBlob has no dependencies. +\o The QQmlDataBlob has an error set. +\o All the QQmlDataBlob's dependencies are themselves "done()". +\endlist + +Thus QQmlDataBlob::done() will always eventually be called, even if the blob has an error set. +*/ + +/*! +Create a new QQmlDataLoader for \a engine. +*/ +QQmlDataLoader::QQmlDataLoader(QQmlEngine *engine) +: m_engine(engine), m_thread(new QQmlDataLoaderThread(this)) +{ +} + +/*! \internal */ +QQmlDataLoader::~QQmlDataLoader() +{ + for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) + (*iter)->release(); + + m_thread->shutdown(); + delete m_thread; +} + +void QQmlDataLoader::lock() +{ + m_thread->lock(); +} + +void QQmlDataLoader::unlock() +{ + m_thread->unlock(); +} + +/*! +Load the provided \a blob from the network or filesystem. + +The loader must be locked. +*/ +void QQmlDataLoader::load(QQmlDataBlob *blob, Mode mode) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), + m_thread->isThisThread()?"Compile":"Engine"); +#endif + + Q_ASSERT(blob->status() == QQmlDataBlob::Null); + Q_ASSERT(blob->m_manager == 0); + + blob->m_data.setStatus(QQmlDataBlob::Loading); + blob->m_manager = this; + + if (m_thread->isThisThread()) { + unlock(); + loadThread(blob); + lock(); + } else if (mode == PreferSynchronous) { + unlock(); + m_thread->load(blob); + lock(); + if (!blob->isCompleteOrError()) + blob->m_data.setIsAsync(true); + } else { + Q_ASSERT(mode == Asynchronous); + blob->m_data.setIsAsync(true); + unlock(); + m_thread->loadAsync(blob); + lock(); + } +} + +/*! +Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. + +The loader must be locked. +*/ +void QQmlDataLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()), + m_thread->isThisThread()?"Compile":"Engine"); +#endif + + Q_ASSERT(blob->status() == QQmlDataBlob::Null); + Q_ASSERT(blob->m_manager == 0); + + blob->m_data.setStatus(QQmlDataBlob::Loading); + blob->m_manager = this; + + if (m_thread->isThisThread()) { + unlock(); + loadWithStaticDataThread(blob, data); + lock(); + } else if (mode == PreferSynchronous) { + unlock(); + m_thread->loadWithStaticData(blob, data); + lock(); + if (!blob->isCompleteOrError()) + blob->m_data.setIsAsync(true); + } else { + Q_ASSERT(mode == Asynchronous); + blob->m_data.setIsAsync(true); + unlock(); + m_thread->loadWithStaticDataAsync(blob, data); + lock(); + } +} + +void QQmlDataLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data) +{ + ASSERT_LOADTHREAD(); + + setData(blob, data); +} + +void QQmlDataLoader::loadThread(QQmlDataBlob *blob) +{ + ASSERT_LOADTHREAD(); + + if (blob->m_url.isEmpty()) { + QQmlError error; + error.setDescription(QLatin1String("Invalid null URL")); + blob->setError(error); + return; + } + + QString lf = QQmlEnginePrivate::urlToLocalFileOrQrc(blob->m_url); + + if (!lf.isEmpty()) { + if (!QQml_isFileCaseCorrect(lf)) { + QQmlError error; + error.setUrl(blob->m_url); + error.setDescription(QLatin1String("File name case mismatch")); + blob->setError(error); + return; + } + QFile file(lf); + if (file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + + blob->m_data.setProgress(0xFF); + if (blob->m_data.isAsync()) + m_thread->callDownloadProgressChanged(blob, 1.); + + setData(blob, data); + } else { + blob->networkError(QNetworkReply::ContentNotFoundError); + } + + } else { + + QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); + QObject *nrp = m_thread->networkReplyProxy(); + QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), + nrp, SLOT(downloadProgress(qint64,qint64))); + QObject::connect(reply, SIGNAL(finished()), + nrp, SLOT(finished())); + m_networkReplies.insert(reply, blob); + + blob->addref(); + } +} + +#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16 + +void QQmlDataLoader::networkReplyFinished(QNetworkReply *reply) +{ + Q_ASSERT(m_thread->isThisThread()); + + reply->deleteLater(); + + QQmlDataBlob *blob = m_networkReplies.take(reply); + + Q_ASSERT(blob); + + blob->m_redirectCount++; + + if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + blob->m_finalUrl = url; + + QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url)); + QObject *nrp = m_thread->networkReplyProxy(); + QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished())); + m_networkReplies.insert(reply, blob); + return; + } + } + + if (reply->error()) { + blob->networkError(reply->error()); + } else { + QByteArray data = reply->readAll(); + setData(blob, data); + } + + blob->release(); +} + +void QQmlDataLoader::networkReplyProgress(QNetworkReply *reply, + qint64 bytesReceived, qint64 bytesTotal) +{ + Q_ASSERT(m_thread->isThisThread()); + + QQmlDataBlob *blob = m_networkReplies.value(reply); + + Q_ASSERT(blob); + + if (bytesTotal != 0) { + quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal)); + blob->m_data.setProgress(progress); + if (blob->m_data.isAsync()) + m_thread->callDownloadProgressChanged(blob, blob->m_data.progress()); + } +} + +/*! +Return the QQmlEngine associated with this loader +*/ +QQmlEngine *QQmlDataLoader::engine() const +{ + return m_engine; +} + +/*! +Call the initializeEngine() method on \a iface. Used by QQmlImportDatabase to ensure it +gets called in the correct thread. +*/ +void QQmlDataLoader::initializeEngine(QQmlExtensionInterface *iface, + const char *uri) +{ + Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread()); + + if (m_thread->isThisThread()) { + m_thread->initializeEngine(iface, uri); + } else { + Q_ASSERT(engine()->thread() == QThread::currentThread()); + iface->initializeEngine(engine(), uri); + } +} + + +void QQmlDataLoader::setData(QQmlDataBlob *blob, const QByteArray &data) +{ + blob->m_inCallback = true; + + blob->dataReceived(data); + + if (!blob->isError() && !blob->isWaiting()) + blob->allDependenciesDone(); + + if (blob->status() != QQmlDataBlob::Error) + blob->m_data.setStatus(QQmlDataBlob::WaitingForDependencies); + + blob->m_inCallback = false; + + blob->tryDone(); +} + +/*! +Constructs a new type loader that uses the given \a engine. +*/ +QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) +: QQmlDataLoader(engine) +{ +} + +/*! +Destroys the type loader, first clearing the cache of any information about +loaded files. +*/ +QQmlTypeLoader::~QQmlTypeLoader() +{ + clearCache(); +} + +/*! +\enum QQmlTypeLoader::Option + +This enum defines the options that control the way type data is handled. + +\value None The default value, indicating that no other options + are enabled. +\value PreserveParser The parser used to handle the type data is preserved + after the data has been parsed. +*/ + +/*! +Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached. +*/ +QQmlTypeData *QQmlTypeLoader::get(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url)))); + + lock(); + + QQmlTypeData *typeData = m_typeCache.value(url); + + if (!typeData) { + typeData = new QQmlTypeData(url, None, this); + m_typeCache.insert(url, typeData); + QQmlDataLoader::load(typeData); + } + + typeData->addref(); + + unlock(); + + return typeData; +} + +/*! +Returns a QQmlTypeData for the given \a data with the provided base \a url. The +QQmlTypeData will not be cached. + +The specified \a options control how the loader handles type data. +*/ +QQmlTypeData *QQmlTypeLoader::get(const QByteArray &data, const QUrl &url, Options options) +{ + lock(); + + QQmlTypeData *typeData = new QQmlTypeData(url, options, this); + QQmlDataLoader::loadWithStaticData(typeData, data); + + unlock(); + + return typeData; +} + +/*! +Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached. +*/ +QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url)))); + + lock(); + + QQmlScriptBlob *scriptBlob = m_scriptCache.value(url); + + if (!scriptBlob) { + scriptBlob = new QQmlScriptBlob(url, this); + m_scriptCache.insert(url, scriptBlob); + QQmlDataLoader::load(scriptBlob); + } + + scriptBlob->addref(); + + unlock(); + + return scriptBlob; +} + +/*! +Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached. +*/ +QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url)))); + + lock(); + + QQmlQmldirData *qmldirData = m_qmldirCache.value(url); + + if (!qmldirData) { + qmldirData = new QQmlQmldirData(url); + m_qmldirCache.insert(url, qmldirData); + QQmlDataLoader::load(qmldirData); + } + + qmldirData->addref(); + + unlock(); + + return qmldirData; +} + +/*! +Returns the absolute filename of path via a directory cache for files named +"qmldir", "*.qml", "*.js", and plugins. +Returns a empty string if the path does not exist. +*/ +QString QQmlTypeLoader::absoluteFilePath(const QString &path) +{ + if (path.isEmpty()) + return QString(); + if (path.at(0) == QLatin1Char(':')) { + // qrc resource + QFileInfo fileInfo(path); + return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); + } +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString lowPath = path.toLower(); + int lastSlash = lowPath.lastIndexOf(QLatin1Char('/')); + QString dirPath = lowPath.left(lastSlash); +#else + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QStringRef dirPath(&path, 0, lastSlash); +#endif + + StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); + if (!fileSet) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QHashedString dirPathString(dirPath); +#else + QHashedString dirPathString(dirPath.toString()); +#endif + StringSet *files = qmlFilesInDirectory(dirPathString); + m_importDirCache.insert(dirPathString, files); + fileSet = m_importDirCache.value(dirPathString); + } + if (!(*fileSet)) + return QString(); + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString(); +#else + QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString(); +#endif + if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':')) + absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath(); + + return absoluteFilePath; +} + +/*! +Returns true if the path is a directory via a directory cache. Cache is +shared with absoluteFilePath(). +*/ +bool QQmlTypeLoader::directoryExists(const QString &path) +{ + if (path.isEmpty()) + return false; + if (path.at(0) == QLatin1Char(':')) { + // qrc resource + QFileInfo fileInfo(path); + return fileInfo.exists() && fileInfo.isDir(); + } + + int length = path.length(); + if (path.endsWith(QLatin1Char('/'))) + --length; +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString dirPath = path.left(length).toLower(); +#else + QStringRef dirPath(&path, 0, length); +#endif + + StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); + if (!fileSet) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QHashedString dirPathString(dirPath); +#else + QHashedString dirPathString(dirPath.toString()); +#endif + StringSet *files = qmlFilesInDirectory(dirPathString); + m_importDirCache.insert(dirPathString, files); + fileSet = m_importDirCache.value(dirPathString); + } + + return (*fileSet); +} + + +/*! +Return a QQmlDirParser for absoluteFilePath. The QQmlDirParser may be cached. +*/ +const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &absoluteFilePath) +{ + QQmlDirParser *qmldirParser; + QQmlDirParser **val = m_importQmlDirCache.value(absoluteFilePath); + if (!val) { + qmldirParser = new QQmlDirParser; + qmldirParser->setFileSource(absoluteFilePath); + qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath)); + qmldirParser->parse(); + m_importQmlDirCache.insert(absoluteFilePath, qmldirParser); + } else { + qmldirParser = *val; + } + + return qmldirParser; +} + + +/*! +Clears cached information about loaded files, including any type data, scripts +and qmldir information. +*/ +void QQmlTypeLoader::clearCache() +{ + for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) + (*iter)->release(); + for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) + (*iter)->release(); + for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) + (*iter)->release(); + qDeleteAll(m_importDirCache); + qDeleteAll(m_importQmlDirCache); + + m_typeCache.clear(); + m_scriptCache.clear(); + m_qmldirCache.clear(); + m_importDirCache.clear(); + m_importQmlDirCache.clear(); +} + + +QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options, + QQmlTypeLoader *manager) +: QQmlDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false), + m_compiledData(0), m_typeLoader(manager) +{ +} + +QQmlTypeData::~QQmlTypeData() +{ + for (int ii = 0; ii < m_scripts.count(); ++ii) + m_scripts.at(ii).script->release(); + for (int ii = 0; ii < m_qmldirs.count(); ++ii) + m_qmldirs.at(ii)->release(); + for (int ii = 0; ii < m_types.count(); ++ii) + if (m_types.at(ii).typeData) m_types.at(ii).typeData->release(); + if (m_compiledData) + m_compiledData->release(); +} + +QQmlTypeLoader *QQmlTypeData::typeLoader() const +{ + return m_typeLoader; +} + +const QQmlImports &QQmlTypeData::imports() const +{ + return m_imports; +} + +const QQmlScript::Parser &QQmlTypeData::parser() const +{ + return scriptParser; +} + +const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const +{ + return m_types; +} + +const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const +{ + return m_scripts; +} + +const QSet<QString> &QQmlTypeData::namespaces() const +{ + return m_namespaces; +} + +QQmlCompiledData *QQmlTypeData::compiledData() const +{ + if (m_compiledData) + m_compiledData->addref(); + + return m_compiledData; +} + +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)); +} + +void QQmlTypeData::done() +{ + // Check all script dependencies for errors + for (int ii = 0; !isError() && 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(finalUrl()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); + errors.prepend(error); + setError(errors); + } + } + + // Check all type dependencies for errors + for (int ii = 0; !isError() && ii < m_types.count(); ++ii) { + const TypeReference &type = m_types.at(ii); + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + QString typeName = scriptParser.referencedTypes().at(ii)->name; + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(finalUrl()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + } + } + + // Compile component + if (!isError()) + compile(); + + if (!(m_options & QQmlTypeLoader::PreserveParser)) + scriptParser.clear(); +} + +void QQmlTypeData::completed() +{ + // Notify callbacks + while (!m_callbacks.isEmpty()) { + TypeDataCallback *callback = m_callbacks.takeFirst(); + callback->typeDataReady(this); + } +} + +void QQmlTypeData::dataReceived(const QByteArray &data) +{ + if (!scriptParser.parse(data, finalUrl(), finalUrlString())) { + setError(scriptParser.errors()); + return; + } + + m_imports.setBaseUrl(finalUrl(), finalUrlString()); + + foreach (const QQmlScript::Import &import, scriptParser.imports()) { + if (import.type == QQmlScript::Import::File && import.qualifier.isEmpty()) { + QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); + if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) { + QQmlQmldirData *data = typeLoader()->getQmldir(importUrl); + addDependency(data); + m_qmldirs << data; + } + } else if (import.type == QQmlScript::Import::Script) { + QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); + QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); + addDependency(blob); + + ScriptReference ref; + ref.location = import.location.start; + ref.qualifier = import.qualifier; + ref.script = blob; + m_scripts << ref; + + } + } + + if (!finalUrl().scheme().isEmpty()) { + QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) { + QQmlQmldirData *data = typeLoader()->getQmldir(importUrl); + addDependency(data); + m_qmldirs << data; + } + } +} + +void QQmlTypeData::allDependenciesDone() +{ + if (!m_typesResolved) { + 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); + } +} + +void QQmlTypeData::compile() +{ + Q_ASSERT(m_compiledData == 0); + QQmlProfilerService::startRange(QQmlProfilerService::Compiling); + + m_compiledData = new QQmlCompiledData(typeLoader()->engine()); + m_compiledData->url = finalUrl(); + m_compiledData->name = finalUrlString(); + QQmlProfilerService::rangeLocation(QQmlProfilerService::Compiling, QUrl(m_compiledData->name),1,1); + QQmlProfilerService::rangeData(QQmlProfilerService::Compiling, m_compiledData->name); + + QQmlCompiler compiler(&scriptParser._pool); + if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { + setError(compiler.errors()); + m_compiledData->release(); + m_compiledData = 0; + } + QQmlProfilerService::endRange(QQmlProfilerService::Compiling); +} + +void QQmlTypeData::resolveTypes() +{ + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_typeLoader->engine()); + QQmlImportDatabase *importDatabase = &ep->importDatabase; + + // For local urls, add an implicit import "." as first (most overridden) lookup. + // This will also trigger the loading of the qmldir and the import of any native + // types from available plugins. + QList<QQmlError> errors; + if (QQmlQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) { + m_imports.addImport(importDatabase, QLatin1String("."), + QString(), -1, -1, QQmlScript::Import::File, + qmldir->dirComponents(), &errors); + } else { + m_imports.addImport(importDatabase, QLatin1String("."), + QString(), -1, -1, QQmlScript::Import::File, + QQmlDirComponents(), &errors); + } + + // remove any errors which are due to the implicit import which aren't real errors. + // for example, if the implicitly included qmldir file doesn't exist, that is not an error. + QList<QQmlError> realErrors; + for (int i = 0; i < errors.size(); ++i) { + if (errors.at(i).description() != QQmlImportDatabase::tr("import \".\" has no qmldir and no namespace") + && errors.at(i).description() != QQmlImportDatabase::tr("\".\": no such directory")) { + realErrors.prepend(errors.at(i)); // this is a real error. + } + } + + // report any real errors which occurred during plugin loading or qmldir parsing. + if (!realErrors.isEmpty()) { + setError(realErrors); + return; + } + + foreach (const QQmlScript::Import &import, scriptParser.imports()) { + QQmlDirComponents qmldircomponentsnetwork; + if (import.type == QQmlScript::Import::Script) + continue; + + if (import.type == QQmlScript::Import::File && import.qualifier.isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); + if (QQmlQmldirData *qmldir = qmldirForUrl(qmldirUrl)) + qmldircomponentsnetwork = qmldir->dirComponents(); + } + + int vmaj = -1; + int vmin = -1; + import.extractVersion(&vmaj, &vmin); + + QList<QQmlError> errors; + if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, + vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) { + QQmlError error; + 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_imports.baseUrl()); + error.setLine(import.location.start.line); + error.setColumn(import.location.start.column); + errors.prepend(error); // put it back on the list after filling out information. + + setError(errors); + return; + } + } + + // Add any imported scripts to our resolved set + foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts()) + { + QQmlScriptBlob *blob = typeLoader()->getScript(script.location); + addDependency(blob); + + ScriptReference ref; + //ref.location = ... + ref.qualifier = script.nameSpace; + if (!script.qualifier.isEmpty()) + { + ref.qualifier.prepend(script.qualifier + QLatin1Char('.')); + + // Add a reference to the enclosing namespace + m_namespaces.insert(script.qualifier); + } + + ref.script = blob; + m_scripts << ref; + } + + foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) { + TypeReference ref; + + QString url; + int majorVersion; + int minorVersion; + QQmlImportedNamespace *typeNamespace = 0; + QList<QQmlError> errors; + + if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion, + &typeNamespace, &errors) || typeNamespace) { + // Known to not be a type: + // - known to be a namespace (Namespace {}) + // - type with unknown namespace (UnknownNamespace.SomeType {}) + QQmlError error; + QString userTypeName = parserRef->name; + userTypeName.replace(QLatin1Char('/'),QLatin1Char('.')); + if (typeNamespace) { + error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName)); + } 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_imports.baseUrl()); + error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description())); + } + + if (!parserRef->refObjects.isEmpty()) { + QQmlScript::Object *obj = parserRef->refObjects.first(); + error.setLine(obj->location.start.line); + error.setColumn(obj->location.start.column); + } + + errors.prepend(error); + setError(errors); + return; + } + + if (ref.type) { + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; + } else { + ref.typeData = typeLoader()->get(QUrl(url)); + addDependency(ref.typeData); + } + + if (parserRef->refObjects.count()) + ref.location = parserRef->refObjects.first()->location.start; + + m_types << ref; + } +} + +QQmlQmldirData *QQmlTypeData::qmldirForUrl(const QUrl &url) +{ + for (int ii = 0; ii < m_qmldirs.count(); ++ii) { + if (m_qmldirs.at(ii)->url() == url) + return m_qmldirs.at(ii); + } + return 0; +} + +QQmlScriptData::QQmlScriptData() +: importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false) +{ +} + +QQmlScriptData::~QQmlScriptData() +{ +} + +void QQmlScriptData::clear() +{ + if (importCache) { + importCache->release(); + importCache = 0; + } + + for (int ii = 0; ii < scripts.count(); ++ii) + scripts.at(ii)->release(); + scripts.clear(); + + qPersistentDispose(m_program); + qPersistentDispose(m_value); + + // An addref() was made when the QQmlCleanup was added to the engine. + release(); +} + +QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) +: QQmlDataBlob(url, JavaScriptFile), m_pragmas(QQmlScript::Object::ScriptBlock::None), + m_imports(loader), m_scriptData(0), m_typeLoader(loader) +{ +} + +QQmlScriptBlob::~QQmlScriptBlob() +{ + if (m_scriptData) { + m_scriptData->release(); + m_scriptData = 0; + } +} + +QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const +{ + return m_pragmas; +} + +QQmlTypeLoader *QQmlScriptBlob::typeLoader() const +{ + return m_typeLoader; +} + +const QQmlImports &QQmlScriptBlob::imports() const +{ + return m_imports; +} + +QQmlScriptData *QQmlScriptBlob::scriptData() const +{ + return m_scriptData; +} + +void QQmlScriptBlob::dataReceived(const QByteArray &data) +{ + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_typeLoader->engine()); + QQmlImportDatabase *importDatabase = &ep->importDatabase; + + m_source = QString::fromUtf8(data); + + QQmlScript::Parser::JavaScriptMetaData metadata = + QQmlScript::Parser::extractMetaData(m_source); + + m_imports.setBaseUrl(finalUrl(), finalUrlString()); + + m_pragmas = metadata.pragmas; + + foreach (const QQmlScript::Import &import, metadata.imports) { + Q_ASSERT(import.type != QQmlScript::Import::File); + + if (import.type == QQmlScript::Import::Script) { + QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); + QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); + addDependency(blob); + + ScriptReference ref; + ref.location = import.location.start; + ref.qualifier = import.qualifier; + ref.script = blob; + m_scripts << ref; + } else { + Q_ASSERT(import.type == QQmlScript::Import::Library); + int vmaj = -1; + int vmin = -1; + import.extractVersion(&vmaj, &vmin); + + QList<QQmlError> errors; + if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin, + import.type, QQmlDirComponents(), &errors)) { + QQmlError error = errors.takeFirst(); + // description should be set by addImport(). + error.setUrl(m_imports.baseUrl()); + error.setLine(import.location.start.line); + error.setColumn(import.location.start.column); + errors.prepend(error); + + setError(errors); + return; + } + } + } +} + +void QQmlScriptBlob::done() +{ + // Check all script dependencies for errors + for (int ii = 0; !isError() && 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(finalUrl()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString())); + errors.prepend(error); + setError(errors); + } + } + + if (isError()) + return; + + QQmlEngine *engine = typeLoader()->engine(); + m_scriptData = new QQmlScriptData(); + m_scriptData->url = finalUrl(); + m_scriptData->urlString = finalUrlString(); + m_scriptData->importCache = new QQmlTypeNameCache(); + + for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + + m_scriptData->scripts.append(script.script); + m_scriptData->importCache->add(script.qualifier, ii); + } + + m_imports.populateCache(m_scriptData->importCache, engine); + + m_scriptData->pragmas = m_pragmas; + m_scriptData->m_programSource = m_source.toUtf8(); + m_source.clear(); +} + +QQmlQmldirData::QQmlQmldirData(const QUrl &url) +: QQmlDataBlob(url, QmldirFile) +{ +} + +const QQmlDirComponents &QQmlQmldirData::dirComponents() const +{ + return m_components; +} + +void QQmlQmldirData::dataReceived(const QByteArray &data) +{ + QQmlDirParser parser; + parser.setSource(QString::fromUtf8(data)); + parser.parse(); + m_components = parser.components(); +} + +QT_END_NAMESPACE + +#include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h new file mode 100644 index 0000000000..0dd7adecac --- /dev/null +++ b/src/qml/qml/qqmltypeloader_p.h @@ -0,0 +1,435 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADER_P_H +#define QQMLTYPELOADER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qatomic.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtQml/qqmlerror.h> +#include <QtQml/qqmlengine.h> + +#include <private/qv8_p.h> +#include <private/qhashedstring_p.h> +#include <private/qqmlscript_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmlcleanup_p.h> +#include <private/qqmldirparser_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlScriptData; +class QQmlScriptBlob; +class QQmlQmldirData; +class QQmlTypeLoader; +class QQmlCompiledData; +class QQmlComponentPrivate; +class QQmlTypeData; +class QQmlDataLoader; +class QQmlExtensionInterface; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount +{ +public: + enum Status { + Null, // Prior to QQmlDataLoader::load() + Loading, // Prior to data being received and dataReceived() being called + WaitingForDependencies, // While there are outstanding addDependency()s + Complete, // Finished + Error // Error + }; + + enum Type { + QmlFile, + JavaScriptFile, + QmldirFile + }; + + QQmlDataBlob(const QUrl &, Type); + virtual ~QQmlDataBlob(); + + 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; + QUrl finalUrl() const; + QString finalUrlString() const; + + QList<QQmlError> errors() const; + +protected: + // Can be called from within callbacks + void setError(const QQmlError &); + void setError(const QList<QQmlError> &errors); + void addDependency(QQmlDataBlob *); + + // Callbacks made in load thread + virtual void dataReceived(const QByteArray &) = 0; + virtual void done(); + virtual void networkError(QNetworkReply::NetworkError); + virtual void dependencyError(QQmlDataBlob *); + virtual void dependencyComplete(QQmlDataBlob *); + virtual void allDependenciesDone(); + + // Callbacks made in main thread + virtual void downloadProgressChanged(qreal); + virtual void completed(); +private: + friend class QQmlDataLoader; + friend class QQmlDataLoaderThread; + + 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_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. + QList<QQmlDataBlob *> m_waitingFor; + + // Manager that is currently fetching data for me + QQmlDataLoader *m_manager; + int m_redirectCount:30; + bool m_inCallback:1; + bool m_isDone:1; +}; + +class QQmlDataLoaderThread; +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlDataLoader +{ +public: + QQmlDataLoader(QQmlEngine *); + ~QQmlDataLoader(); + + void lock(); + void unlock(); + + bool isConcurrent() const { return true; } + + enum Mode { PreferSynchronous, Asynchronous }; + + void load(QQmlDataBlob *, Mode = PreferSynchronous); + void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous); + + QQmlEngine *engine() const; + void initializeEngine(QQmlExtensionInterface *, const char *); + +private: + friend class QQmlDataBlob; + friend class QQmlDataLoaderThread; + friend class QQmlDataLoaderNetworkReplyProxy; + + void loadThread(QQmlDataBlob *); + void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &); + void networkReplyFinished(QNetworkReply *); + void networkReplyProgress(QNetworkReply *, qint64, qint64); + + typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies; + + void setData(QQmlDataBlob *, const QByteArray &); + + QQmlEngine *m_engine; + QQmlDataLoaderThread *m_thread; + NetworkReplies m_networkReplies; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlTypeLoader : public QQmlDataLoader +{ + Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) +public: + QQmlTypeLoader(QQmlEngine *); + ~QQmlTypeLoader(); + + enum Option { + None, + PreserveParser + }; + Q_DECLARE_FLAGS(Options, Option) + + QQmlTypeData *get(const QUrl &url); + QQmlTypeData *get(const QByteArray &, const QUrl &url, Options = None); + void clearCache(); + + QQmlScriptBlob *getScript(const QUrl &); + QQmlQmldirData *getQmldir(const QUrl &); + + QString absoluteFilePath(const QString &path); + bool directoryExists(const QString &path); + const QQmlDirParser *qmlDirParser(const QString &absoluteFilePath); +private: + typedef QHash<QUrl, QQmlTypeData *> TypeCache; + typedef QHash<QUrl, QQmlScriptBlob *> ScriptCache; + typedef QHash<QUrl, QQmlQmldirData *> QmldirCache; + typedef QStringHash<bool> StringSet; + typedef QStringHash<StringSet*> ImportDirCache; + typedef QStringHash<QQmlDirParser*> ImportQmlDirCache; + + TypeCache m_typeCache; + ScriptCache m_scriptCache; + QmldirCache m_qmldirCache; + ImportDirCache m_importDirCache; + ImportQmlDirCache m_importQmlDirCache; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlTypeLoader::Options) + +class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlDataBlob +{ +public: + struct TypeReference + { + TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0) {} + + QQmlScript::Location location; + QQmlType *type; + int majorVersion; + int minorVersion; + QQmlTypeData *typeData; + }; + + struct ScriptReference + { + ScriptReference() : script(0) {} + + QQmlScript::Location location; + QString qualifier; + QQmlScriptBlob *script; + }; + + QQmlTypeData(const QUrl &, QQmlTypeLoader::Options, QQmlTypeLoader *); + ~QQmlTypeData(); + + QQmlTypeLoader *typeLoader() const; + + const QQmlImports &imports() const; + const QQmlScript::Parser &parser() const; + + const QList<TypeReference> &resolvedTypes() const; + const QList<ScriptReference> &resolvedScripts() const; + const QSet<QString> &namespaces() const; + + QQmlCompiledData *compiledData() const; + + // Used by QQmlComponent to get notifications + struct TypeDataCallback { + ~TypeDataCallback() {} + virtual void typeDataProgress(QQmlTypeData *, qreal) {} + virtual void typeDataReady(QQmlTypeData *) {} + }; + void registerCallback(TypeDataCallback *); + void unregisterCallback(TypeDataCallback *); + +protected: + virtual void done(); + virtual void completed(); + virtual void dataReceived(const QByteArray &); + virtual void allDependenciesDone(); + virtual void downloadProgressChanged(qreal); + +private: + void resolveTypes(); + void compile(); + + QQmlTypeLoader::Options m_options; + + QQmlQmldirData *qmldirForUrl(const QUrl &); + + QQmlScript::Parser scriptParser; + QQmlImports m_imports; + + QList<ScriptReference> m_scripts; + QList<QQmlQmldirData *> m_qmldirs; + + QSet<QString> m_namespaces; + + QList<TypeReference> m_types; + bool m_typesResolved:1; + + QQmlCompiledData *m_compiledData; + + QList<TypeDataCallback *> m_callbacks; + + QQmlTypeLoader *m_typeLoader; +}; + +// 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 +{ +public: + QQmlScriptData(); + ~QQmlScriptData(); + + QUrl url; + QString urlString; + QQmlTypeNameCache *importCache; + QList<QQmlScriptBlob *> scripts; + QQmlScript::Object::ScriptBlock::Pragmas pragmas; + + bool isInitialized() const { return hasEngine(); } + void initialize(QQmlEngine *); + +protected: + virtual void clear(); // From QQmlCleanup + +private: + friend class QQmlVME; + friend class QQmlScriptBlob; + + bool m_loaded; + QByteArray m_programSource; + v8::Persistent<v8::Script> m_program; + v8::Persistent<v8::Object> m_value; +}; + +class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlDataBlob +{ +public: + QQmlScriptBlob(const QUrl &, QQmlTypeLoader *); + ~QQmlScriptBlob(); + + struct ScriptReference + { + ScriptReference() : script(0) {} + + QQmlScript::Location location; + QString qualifier; + QQmlScriptBlob *script; + }; + + QQmlScript::Object::ScriptBlock::Pragmas pragmas() const; + + QQmlTypeLoader *typeLoader() const; + const QQmlImports &imports() const; + + QQmlScriptData *scriptData() const; + +protected: + virtual void dataReceived(const QByteArray &); + virtual void done(); + +private: + QQmlScript::Object::ScriptBlock::Pragmas m_pragmas; + QString m_source; + + QQmlImports m_imports; + QList<ScriptReference> m_scripts; + QQmlScriptData *m_scriptData; + + QQmlTypeLoader *m_typeLoader; +}; + +class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlDataBlob +{ +public: + QQmlQmldirData(const QUrl &); + + const QQmlDirComponents &dirComponents() const; + +protected: + virtual void dataReceived(const QByteArray &); + +private: + QQmlDirComponents m_components; + +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADER_P_H diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp new file mode 100644 index 0000000000..f9d3e7704c --- /dev/null +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypenamecache_p.h" + +#include "qqmlengine_p.h" + +QT_BEGIN_NAMESPACE + +QQmlTypeNameCache::QQmlTypeNameCache() +{ +} + +QQmlTypeNameCache::~QQmlTypeNameCache() +{ +} + +void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, const QHashedString &nameSpace) +{ + Import import; + import.scriptIndex = importedScriptIndex; + + if (nameSpace.length() != 0) { + Import *i = m_namedImports.value(nameSpace); + Q_ASSERT(i != 0); + m_namespacedImports[i].insert(name, import); + return; + } + + if (m_namedImports.contains(name)) + return; + + m_namedImports.insert(name, import); +} + +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) +{ + Result result = query(m_namedImports, name); + + if (!result.isValid()) + result = typeSearch(m_anonymousImports, name); + + return result; +} + +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, + const void *importNamespace) +{ + Q_ASSERT(importNamespace); + const Import *i = static_cast<const Import *>(importNamespace); + Q_ASSERT(i->scriptIndex == -1); + + return typeSearch(i->modules, name); +} + +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedV8String &name) +{ + Result result = query(m_namedImports, name); + + if (!result.isValid()) + result = typeSearch(m_anonymousImports, name); + + return result; +} + +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedV8String &name, const void *importNamespace) +{ + Q_ASSERT(importNamespace); + const Import *i = static_cast<const Import *>(importNamespace); + Q_ASSERT(i->scriptIndex == -1); + + QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.find(i); + if (it != m_namespacedImports.constEnd()) + return query(*it, name); + + return typeSearch(i->modules, name); +} + +QQmlMetaType::ModuleApiInstance *QQmlTypeNameCache::moduleApi(const void *importNamespace) +{ + Q_ASSERT(importNamespace); + const Import *i = static_cast<const Import *>(importNamespace); + Q_ASSERT(i->scriptIndex == -1); + + return i->moduleApi; +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h new file mode 100644 index 0000000000..a9268db37a --- /dev/null +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPENAMECACHE_P_H +#define QQMLTYPENAMECACHE_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 "qqmlcleanup_p.h" +#include "qqmlmetatype_p.h" + +#include <private/qhashedstring_p.h> + +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class QQmlType; +class QQmlEngine; +class QQmlTypeNameCache : public QQmlRefCount +{ +public: + QQmlTypeNameCache(); + virtual ~QQmlTypeNameCache(); + + inline bool isEmpty() const; + + void add(const QHashedString &name, int sciptIndex = -1, const QHashedString &nameSpace = QHashedString()); + + struct Result { + inline Result(); + inline Result(const void *importNamespace); + inline Result(QQmlType *type); + inline Result(int scriptIndex); + inline Result(const Result &); + + inline bool isValid() const; + + QQmlType *type; + const void *importNamespace; + int scriptIndex; + }; + Result query(const QHashedStringRef &); + Result query(const QHashedStringRef &, const void *importNamespace); + Result query(const QHashedV8String &); + Result query(const QHashedV8String &, const void *importNamespace); + QQmlMetaType::ModuleApiInstance *moduleApi(const void *importNamespace); + +private: + friend class QQmlImports; + + struct Import { + inline Import(); + // Imported module + QQmlMetaType::ModuleApiInstance *moduleApi; + QVector<QQmlTypeModuleVersion> modules; + + // Or, imported script + int scriptIndex; + }; + + template<typename Key> + Result query(const QStringHash<Import> &imports, Key key) + { + Import *i = imports.value(key); + if (i) { + if (i->scriptIndex != -1) { + return Result(i->scriptIndex); + } else { + return Result(static_cast<const void *>(i)); + } + } + + return Result(); + } + + template<typename Key> + Result typeSearch(const QVector<QQmlTypeModuleVersion> &modules, Key key) + { + QVector<QQmlTypeModuleVersion>::const_iterator end = modules.constEnd(); + for (QVector<QQmlTypeModuleVersion>::const_iterator it = modules.constBegin(); it != end; ++it) { + if (QQmlType *type = it->type(key)) + return Result(type); + } + + return Result(); + } + + QStringHash<Import> m_namedImports; + QMap<const Import *, QStringHash<Import> > m_namespacedImports; + QVector<QQmlTypeModuleVersion> m_anonymousImports; + + QQmlEngine *engine; +}; + +QQmlTypeNameCache::Result::Result() +: type(0), importNamespace(0), scriptIndex(-1) +{ +} + +QQmlTypeNameCache::Result::Result(const void *importNamespace) +: type(0), importNamespace(importNamespace), scriptIndex(-1) +{ +} + +QQmlTypeNameCache::Result::Result(QQmlType *type) +: type(type), importNamespace(0), scriptIndex(-1) +{ +} + +QQmlTypeNameCache::Result::Result(int scriptIndex) +: type(0), importNamespace(0), scriptIndex(scriptIndex) +{ +} + +QQmlTypeNameCache::Result::Result(const Result &o) +: type(o.type), importNamespace(o.importNamespace), scriptIndex(o.scriptIndex) +{ +} + +bool QQmlTypeNameCache::Result::isValid() const +{ + return type || importNamespace || scriptIndex != -1; +} + +QQmlTypeNameCache::Import::Import() +: moduleApi(0), scriptIndex(-1) +{ +} + +bool QQmlTypeNameCache::isEmpty() const +{ + return m_namedImports.isEmpty() && m_anonymousImports.isEmpty(); +} + +QT_END_NAMESPACE + +#endif // QQMLTYPENAMECACHE_P_H + diff --git a/src/qml/qml/qqmltypenotavailable.cpp b/src/qml/qml/qqmltypenotavailable.cpp new file mode 100644 index 0000000000..6eb891af71 --- /dev/null +++ b/src/qml/qml/qqmltypenotavailable.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltypenotavailable_p.h" + +QT_BEGIN_NAMESPACE + +int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message) +{ + return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(uri,versionMajor,versionMinor,qmlName,message); +} + +QQmlTypeNotAvailable::QQmlTypeNotAvailable() { } + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h new file mode 100644 index 0000000000..d0618a0686 --- /dev/null +++ b/src/qml/qml/qqmltypenotavailable_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPENOTAVAILABLE_H +#define QQMLTYPENOTAVAILABLE_H + +#include <qqml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlTypeNotAvailable : public QObject { + Q_OBJECT +public: + QQmlTypeNotAvailable(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlTypeNotAvailable) + +QT_END_HEADER + +#endif // QQMLTYPENOTAVAILABLE_H diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp new file mode 100644 index 0000000000..4ade00f9b4 --- /dev/null +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -0,0 +1,868 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlvaluetype_p.h" + +#include "qqmlmetatype_p.h" +#include <private/qfont_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +template<typename T> +int qmlRegisterValueTypeEnums(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + + QQmlPrivate::RegisterType type = { + 0, + + qRegisterMetaType<T *>(pointerName.constData()), 0, 0, 0, + + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + 0, 0, + + 0, 0, 0, + + 0, 0, + + 0, + 0 + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + +QQmlValueTypeFactory::QQmlValueTypeFactory() +{ + // ### Optimize + for (unsigned int ii = 0; ii < (QVariant::UserType - 1); ++ii) + valueTypes[ii] = valueType(ii); +} + +QQmlValueTypeFactory::~QQmlValueTypeFactory() +{ + for (unsigned int ii = 0; ii < (QVariant::UserType - 1); ++ii) + delete valueTypes[ii]; +} + +bool QQmlValueTypeFactory::isValueType(int idx) +{ + if ((uint)idx < QVariant::UserType + && idx != QVariant::StringList + && idx != QMetaType::QObjectStar + && idx != QMetaType::QWidgetStar + && idx != QMetaType::VoidStar + && idx != QMetaType::QVariant) { + return true; + } + return false; +} + +void QQmlValueTypeFactory::registerBaseTypes(const char *uri, int versionMajor, int versionMinor) +{ + qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing"); + qmlRegisterValueTypeEnums<QQmlFontValueType>(uri, versionMajor, versionMinor, "Font"); +} + +void QQmlValueTypeFactory::registerValueTypes() +{ + registerBaseTypes("QtQuick", 2, 0); +} + +QQmlValueType *QQmlValueTypeFactory::valueType(int t) +{ + QQmlValueType *rv = 0; + + switch (t) { + case QVariant::Point: + rv = new QQmlPointValueType; + break; + case QVariant::PointF: + rv = new QQmlPointFValueType; + break; + case QVariant::Size: + rv = new QQmlSizeValueType; + break; + case QVariant::SizeF: + rv = new QQmlSizeFValueType; + break; + case QVariant::Rect: + rv = new QQmlRectValueType; + break; + case QVariant::RectF: + rv = new QQmlRectFValueType; + break; + case QVariant::Vector2D: + rv = new QQmlVector2DValueType; + break; + case QVariant::Vector3D: + rv = new QQmlVector3DValueType; + break; + case QVariant::Vector4D: + rv = new QQmlVector4DValueType; + break; + case QVariant::Quaternion: + rv = new QQmlQuaternionValueType; + break; + case QVariant::Matrix4x4: + rv = new QQmlMatrix4x4ValueType; + break; + case QVariant::EasingCurve: + rv = new QQmlEasingValueType; + break; + case QVariant::Font: + rv = new QQmlFontValueType; + break; + case QVariant::Color: + rv = new QQmlColorValueType; + break; + default: + break; + } + + Q_ASSERT(!rv || rv->metaObject()->propertyCount() < 32); + return rv; +} + +QQmlValueType::QQmlValueType(QObject *parent) +: QObject(parent) +{ +} + +#define QML_VALUETYPE_READWRITE(name, cpptype, var) \ + QQml ## name ## ValueType::QQml ## name ## ValueType(QObject *parent) \ + : QQmlValueType(parent) \ + { \ + } \ + void QQml ## name ## ValueType::read(QObject *obj, int idx) \ + { \ + void *a[] = { &var, 0 }; \ + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); \ + onLoad(); \ + } \ + void QQml ## name ## ValueType::write(QObject *obj, int idx, \ + QQmlPropertyPrivate::WriteFlags flags) \ + { \ + int status = -1; \ + void *a[] = { &var, 0, &status, &flags }; \ + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); \ + } \ + bool QQml ## name ## ValueType::isEqual(const QVariant &value) const \ + { \ + return QVariant(var) == value; \ + } \ + QVariant QQml ## name ## ValueType::value() \ + { \ + return QVariant(var); \ + } \ + void QQml ## name ## ValueType::setValue(const QVariant &value) \ + { \ + var = qvariant_cast<cpptype>(value); \ + onLoad(); \ + } + +QML_VALUETYPE_READWRITE(PointF, QPointF, point); +QML_VALUETYPE_READWRITE(Point, QPoint, point); +QML_VALUETYPE_READWRITE(SizeF, QSizeF, size); +QML_VALUETYPE_READWRITE(Size, QSize, size); +QML_VALUETYPE_READWRITE(RectF, QRectF, rect); +QML_VALUETYPE_READWRITE(Rect, QRect, rect); +QML_VALUETYPE_READWRITE(Vector2D, QVector2D, vector); +QML_VALUETYPE_READWRITE(Vector3D, QVector3D, vector); +QML_VALUETYPE_READWRITE(Vector4D, QVector4D, vector); +QML_VALUETYPE_READWRITE(Quaternion, QQuaternion, quaternion); +QML_VALUETYPE_READWRITE(Matrix4x4, QMatrix4x4, matrix); +QML_VALUETYPE_READWRITE(Easing, QEasingCurve, easing); +QML_VALUETYPE_READWRITE(Font, QFont, font); +QML_VALUETYPE_READWRITE(Color, QColor, color); + +QString QQmlPointFValueType::toString() const +{ + return QString(QLatin1String("QPointF(%1, %2)")).arg(point.x()).arg(point.y()); +} + +qreal QQmlPointFValueType::x() const +{ + return point.x(); +} + +qreal QQmlPointFValueType::y() const +{ + return point.y(); +} + +void QQmlPointFValueType::setX(qreal x) +{ + point.setX(x); +} + +void QQmlPointFValueType::setY(qreal y) +{ + point.setY(y); +} + +QString QQmlPointValueType::toString() const +{ + return QString(QLatin1String("QPoint(%1, %2)")).arg(point.x()).arg(point.y()); +} + +int QQmlPointValueType::x() const +{ + return point.x(); +} + +int QQmlPointValueType::y() const +{ + return point.y(); +} + +void QQmlPointValueType::setX(int x) +{ + point.setX(x); +} + +void QQmlPointValueType::setY(int y) +{ + point.setY(y); +} + +QString QQmlSizeFValueType::toString() const +{ + return QString(QLatin1String("QSizeF(%1, %2)")).arg(size.width()).arg(size.height()); +} + +qreal QQmlSizeFValueType::width() const +{ + return size.width(); +} + +qreal QQmlSizeFValueType::height() const +{ + return size.height(); +} + +void QQmlSizeFValueType::setWidth(qreal w) +{ + size.setWidth(w); +} + +void QQmlSizeFValueType::setHeight(qreal h) +{ + size.setHeight(h); +} + +QString QQmlSizeValueType::toString() const +{ + return QString(QLatin1String("QSize(%1, %2)")).arg(size.width()).arg(size.height()); +} + +int QQmlSizeValueType::width() const +{ + return size.width(); +} + +int QQmlSizeValueType::height() const +{ + return size.height(); +} + +void QQmlSizeValueType::setWidth(int w) +{ + size.setWidth(w); +} + +void QQmlSizeValueType::setHeight(int h) +{ + size.setHeight(h); +} + +QString QQmlRectFValueType::toString() const +{ + return QString(QLatin1String("QRectF(%1, %2, %3, %4)")).arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); +} + +qreal QQmlRectFValueType::x() const +{ + return rect.x(); +} + +qreal QQmlRectFValueType::y() const +{ + return rect.y(); +} + +void QQmlRectFValueType::setX(qreal x) +{ + rect.moveLeft(x); +} + +void QQmlRectFValueType::setY(qreal y) +{ + rect.moveTop(y); +} + +qreal QQmlRectFValueType::width() const +{ + return rect.width(); +} + +qreal QQmlRectFValueType::height() const +{ + return rect.height(); +} + +void QQmlRectFValueType::setWidth(qreal w) +{ + rect.setWidth(w); +} + +void QQmlRectFValueType::setHeight(qreal h) +{ + rect.setHeight(h); +} + +QString QQmlRectValueType::toString() const +{ + return QString(QLatin1String("QRect(%1, %2, %3, %4)")).arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); +} + +int QQmlRectValueType::x() const +{ + return rect.x(); +} + +int QQmlRectValueType::y() const +{ + return rect.y(); +} + +void QQmlRectValueType::setX(int x) +{ + rect.moveLeft(x); +} + +void QQmlRectValueType::setY(int y) +{ + rect.moveTop(y); +} + +int QQmlRectValueType::width() const +{ + return rect.width(); +} + +int QQmlRectValueType::height() const +{ + return rect.height(); +} + +void QQmlRectValueType::setWidth(int w) +{ + rect.setWidth(w); +} + +void QQmlRectValueType::setHeight(int h) +{ + rect.setHeight(h); +} + +QString QQmlVector2DValueType::toString() const +{ + return QString(QLatin1String("QVector2D(%1, %2)")).arg(vector.x()).arg(vector.y()); +} + +qreal QQmlVector2DValueType::x() const +{ + return vector.x(); +} + +qreal QQmlVector2DValueType::y() const +{ + return vector.y(); +} + +void QQmlVector2DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QQmlVector2DValueType::setY(qreal y) +{ + vector.setY(y); +} + +QString QQmlVector3DValueType::toString() const +{ + return QString(QLatin1String("QVector3D(%1, %2, %3)")).arg(vector.x()).arg(vector.y()).arg(vector.z()); +} + +qreal QQmlVector3DValueType::x() const +{ + return vector.x(); +} + +qreal QQmlVector3DValueType::y() const +{ + return vector.y(); +} + +qreal QQmlVector3DValueType::z() const +{ + return vector.z(); +} + +void QQmlVector3DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QQmlVector3DValueType::setY(qreal y) +{ + vector.setY(y); +} + +void QQmlVector3DValueType::setZ(qreal z) +{ + vector.setZ(z); +} + +QString QQmlVector4DValueType::toString() const +{ + return QString(QLatin1String("QVector4D(%1, %2, %3, %4)")).arg(vector.x()).arg(vector.y()).arg(vector.z()).arg(vector.w()); +} + +qreal QQmlVector4DValueType::x() const +{ + return vector.x(); +} + +qreal QQmlVector4DValueType::y() const +{ + return vector.y(); +} + +qreal QQmlVector4DValueType::z() const +{ + return vector.z(); +} + +qreal QQmlVector4DValueType::w() const +{ + return vector.w(); +} + +void QQmlVector4DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QQmlVector4DValueType::setY(qreal y) +{ + vector.setY(y); +} + +void QQmlVector4DValueType::setZ(qreal z) +{ + vector.setZ(z); +} + +void QQmlVector4DValueType::setW(qreal w) +{ + vector.setW(w); +} + +QString QQmlQuaternionValueType::toString() const +{ + return QString(QLatin1String("QQuaternion(%1, %2, %3, %4)")).arg(quaternion.scalar()).arg(quaternion.x()).arg(quaternion.y()).arg(quaternion.z()); +} + +qreal QQmlQuaternionValueType::scalar() const +{ + return quaternion.scalar(); +} + +qreal QQmlQuaternionValueType::x() const +{ + return quaternion.x(); +} + +qreal QQmlQuaternionValueType::y() const +{ + return quaternion.y(); +} + +qreal QQmlQuaternionValueType::z() const +{ + return quaternion.z(); +} + +void QQmlQuaternionValueType::setScalar(qreal scalar) +{ + quaternion.setScalar(scalar); +} + +void QQmlQuaternionValueType::setX(qreal x) +{ + quaternion.setX(x); +} + +void QQmlQuaternionValueType::setY(qreal y) +{ + quaternion.setY(y); +} + +void QQmlQuaternionValueType::setZ(qreal z) +{ + quaternion.setZ(z); +} + +QString QQmlMatrix4x4ValueType::toString() const +{ + return QString(QLatin1String("QMatrix4x4(%1, %2, %3, %4, %5, %6, %7, %8, %9, %10, %11, %12, %13, %14, %15, %16)")) + .arg(matrix(0, 0)).arg(matrix(0, 1)).arg(matrix(0, 2)).arg(matrix(0, 3)) + .arg(matrix(1, 0)).arg(matrix(1, 1)).arg(matrix(1, 2)).arg(matrix(1, 3)) + .arg(matrix(2, 0)).arg(matrix(2, 1)).arg(matrix(2, 2)).arg(matrix(2, 3)) + .arg(matrix(3, 0)).arg(matrix(3, 1)).arg(matrix(3, 2)).arg(matrix(3, 3)); +} + +QString QQmlEasingValueType::toString() const +{ + return QString(QLatin1String("QEasingCurve(%1, %2, %3, %4)")).arg(easing.type()).arg(easing.amplitude()).arg(easing.overshoot()).arg(easing.period()); +} + +QQmlEasingValueType::Type QQmlEasingValueType::type() const +{ + return (QQmlEasingValueType::Type)easing.type(); +} + +qreal QQmlEasingValueType::amplitude() const +{ + return easing.amplitude(); +} + +qreal QQmlEasingValueType::overshoot() const +{ + return easing.overshoot(); +} + +qreal QQmlEasingValueType::period() const +{ + return easing.period(); +} + +void QQmlEasingValueType::setType(QQmlEasingValueType::Type type) +{ + easing.setType((QEasingCurve::Type)type); +} + +void QQmlEasingValueType::setAmplitude(qreal amplitude) +{ + easing.setAmplitude(amplitude); +} + +void QQmlEasingValueType::setOvershoot(qreal overshoot) +{ + easing.setOvershoot(overshoot); +} + +void QQmlEasingValueType::setPeriod(qreal period) +{ + easing.setPeriod(period); +} + +void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant) +{ + if (customCurveVariant.isEmpty()) + return; + + QVariantList variantList = customCurveVariant; + if ((variantList.count() % 6) == 0) { + bool allRealsOk = true; + QList<qreal> reals; + for (int i = 0; i < variantList.count(); i++) { + bool ok; + const qreal real = variantList.at(i).toReal(&ok); + reals.append(real); + if (!ok) + allRealsOk = false; + } + if (allRealsOk) { + QEasingCurve newEasingCurve(QEasingCurve::BezierSpline); + for (int i = 0; i < reals.count() / 6; i++) { + const qreal c1x = reals.at(i * 6); + const qreal c1y = reals.at(i * 6 + 1); + const qreal c2x = reals.at(i * 6 + 2); + const qreal c2y = reals.at(i * 6 + 3); + const qreal c3x = reals.at(i * 6 + 4); + const qreal c3y = reals.at(i * 6 + 5); + + const QPointF c1(c1x, c1y); + const QPointF c2(c2x, c2y); + const QPointF c3(c3x, c3y); + + newEasingCurve.addCubicBezierSegment(c1, c2, c3); + easing = newEasingCurve; + } + } + } +} + +QVariantList QQmlEasingValueType::bezierCurve() const +{ + QVariantList rv; + QList<QPointF> points = easing.cubicBezierSpline(); + for (int ii = 0; ii < points.count(); ++ii) + rv << QVariant(points.at(ii).x()) << QVariant(points.at(ii).y()); + return rv; +} + +void QQmlFontValueType::onLoad() +{ + pixelSizeSet = false; + pointSizeSet = false; +} + +QString QQmlFontValueType::toString() const +{ + return QString(QLatin1String("QFont(%1)")).arg(font.toString()); +} + +QString QQmlFontValueType::family() const +{ + return font.family(); +} + +void QQmlFontValueType::setFamily(const QString &family) +{ + font.setFamily(family); +} + +bool QQmlFontValueType::bold() const +{ + return font.bold(); +} + +void QQmlFontValueType::setBold(bool b) +{ + font.setBold(b); +} + +QQmlFontValueType::FontWeight QQmlFontValueType::weight() const +{ + return (QQmlFontValueType::FontWeight)font.weight(); +} + +void QQmlFontValueType::setWeight(QQmlFontValueType::FontWeight w) +{ + font.setWeight((QFont::Weight)w); +} + +bool QQmlFontValueType::italic() const +{ + return font.italic(); +} + +void QQmlFontValueType::setItalic(bool b) +{ + font.setItalic(b); +} + +bool QQmlFontValueType::underline() const +{ + return font.underline(); +} + +void QQmlFontValueType::setUnderline(bool b) +{ + font.setUnderline(b); +} + +bool QQmlFontValueType::overline() const +{ + return font.overline(); +} + +void QQmlFontValueType::setOverline(bool b) +{ + font.setOverline(b); +} + +bool QQmlFontValueType::strikeout() const +{ + return font.strikeOut(); +} + +void QQmlFontValueType::setStrikeout(bool b) +{ + font.setStrikeOut(b); +} + +qreal QQmlFontValueType::pointSize() const +{ + if (font.pointSizeF() == -1) { + if (dpi.isNull) + dpi = qt_defaultDpi(); + return font.pixelSize() * qreal(72.) / qreal(dpi); + } + return font.pointSizeF(); +} + +void QQmlFontValueType::setPointSize(qreal size) +{ + if (pixelSizeSet) { + qWarning() << "Both point size and pixel size set. Using pixel size."; + return; + } + + if (size >= 0.0) { + pointSizeSet = true; + font.setPointSizeF(size); + } else { + pointSizeSet = false; + } +} + +int QQmlFontValueType::pixelSize() const +{ + if (font.pixelSize() == -1) { + if (dpi.isNull) + dpi = qt_defaultDpi(); + return (font.pointSizeF() * dpi) / qreal(72.); + } + return font.pixelSize(); +} + +void QQmlFontValueType::setPixelSize(int size) +{ + if (size >0) { + if (pointSizeSet) + qWarning() << "Both point size and pixel size set. Using pixel size."; + font.setPixelSize(size); + pixelSizeSet = true; + } else { + pixelSizeSet = false; + } +} + +QQmlFontValueType::Capitalization QQmlFontValueType::capitalization() const +{ + return (QQmlFontValueType::Capitalization)font.capitalization(); +} + +void QQmlFontValueType::setCapitalization(QQmlFontValueType::Capitalization c) +{ + font.setCapitalization((QFont::Capitalization)c); +} + +qreal QQmlFontValueType::letterSpacing() const +{ + return font.letterSpacing(); +} + +void QQmlFontValueType::setLetterSpacing(qreal size) +{ + font.setLetterSpacing(QFont::AbsoluteSpacing, size); +} + +qreal QQmlFontValueType::wordSpacing() const +{ + return font.wordSpacing(); +} + +void QQmlFontValueType::setWordSpacing(qreal size) +{ + font.setWordSpacing(size); +} + +QString QQmlColorValueType::toString() const +{ + // special case - to maintain behaviour with QtQuick 1.0, we just output normal toString() value. + return QVariant(color).toString(); +} + +qreal QQmlColorValueType::r() const +{ + return color.redF(); +} + +qreal QQmlColorValueType::g() const +{ + return color.greenF(); +} + +qreal QQmlColorValueType::b() const +{ + return color.blueF(); +} + +qreal QQmlColorValueType::a() const +{ + return color.alphaF(); +} + +void QQmlColorValueType::setR(qreal r) +{ + color.setRedF(r); +} + +void QQmlColorValueType::setG(qreal g) +{ + color.setGreenF(g); +} + +void QQmlColorValueType::setB(qreal b) +{ + color.setBlueF(b); +} + +void QQmlColorValueType::setA(qreal a) +{ + color.setAlphaF(a); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h new file mode 100644 index 0000000000..71817c2a2d --- /dev/null +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -0,0 +1,635 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLVALUETYPE_P_H +#define QQMLVALUETYPE_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 "qqmlproperty.h" +#include "qqmlproperty_p.h" +#include "qqmlnullablevalue_p_p.h" + +#include <QtCore/qobject.h> +#include <QtCore/qrect.h> +#include <QtCore/qeasingcurve.h> +#include <QtCore/qvariant.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qquaternion.h> +#include <QtGui/qfont.h> +#include <QtGui/qcolor.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlValueType : public QObject +{ + Q_OBJECT +public: + QQmlValueType(QObject *parent = 0); + virtual void read(QObject *, int) = 0; + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags flags) = 0; + virtual QVariant value() = 0; + virtual void setValue(const QVariant &) = 0; + + virtual QString toString() const = 0; + virtual bool isEqual(const QVariant &value) const = 0; + + inline void onLoad(); +}; + +class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory +{ +public: + QQmlValueTypeFactory(); + ~QQmlValueTypeFactory(); + static bool isValueType(int); + static QQmlValueType *valueType(int); + + static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); + static void registerValueTypes(); + + QQmlValueType *operator[](int idx) const { + if (idx >= (int)QVariant::UserType) return 0; + else return valueTypes[idx]; + } + +private: + QQmlValueType *valueTypes[QVariant::UserType - 1]; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlPointFValueType : public QQmlValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_OBJECT +public: + QQmlPointFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + +private: + QPointF point; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlPointValueType : public QQmlValueType +{ + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int y READ y WRITE setY) + Q_OBJECT +public: + QQmlPointValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + int x() const; + int y() const; + void setX(int); + void setY(int); + +private: + QPoint point; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlSizeFValueType : public QQmlValueType +{ + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_OBJECT +public: + QQmlSizeFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal width() const; + qreal height() const; + void setWidth(qreal); + void setHeight(qreal); + +private: + QSizeF size; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlSizeValueType : public QQmlValueType +{ + Q_PROPERTY(int width READ width WRITE setWidth) + Q_PROPERTY(int height READ height WRITE setHeight) + Q_OBJECT +public: + QQmlSizeValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + int width() const; + int height() const; + void setWidth(int); + void setHeight(int); + +private: + QSize size; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlRectFValueType : public QQmlValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_OBJECT +public: + QQmlRectFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + + qreal width() const; + qreal height() const; + void setWidth(qreal); + void setHeight(qreal); + +private: + QRectF rect; +}; + +// Exported for QtQuick1 +class Q_QML_PRIVATE_EXPORT QQmlRectValueType : public QQmlValueType +{ + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int y READ y WRITE setY) + Q_PROPERTY(int width READ width WRITE setWidth) + Q_PROPERTY(int height READ height WRITE setHeight) + Q_OBJECT +public: + QQmlRectValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + int x() const; + int y() const; + void setX(int); + void setY(int); + + int width() const; + int height() const; + void setWidth(int); + void setHeight(int); + +private: + QRect rect; +}; + +class Q_QML_PRIVATE_EXPORT QQmlVector2DValueType : public QQmlValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_OBJECT +public: + QQmlVector2DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + +private: + QVector2D vector; +}; + +class Q_QML_PRIVATE_EXPORT QQmlVector3DValueType : public QQmlValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_OBJECT +public: + QQmlVector3DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal x() const; + qreal y() const; + qreal z() const; + void setX(qreal); + void setY(qreal); + void setZ(qreal); + +private: + QVector3D vector; +}; + +class Q_QML_PRIVATE_EXPORT QQmlVector4DValueType : public QQmlValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_PROPERTY(qreal w READ w WRITE setW) + Q_OBJECT +public: + QQmlVector4DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal x() const; + qreal y() const; + qreal z() const; + qreal w() const; + void setX(qreal); + void setY(qreal); + void setZ(qreal); + void setW(qreal); + +private: + QVector4D vector; +}; + +class Q_QML_PRIVATE_EXPORT QQmlQuaternionValueType : public QQmlValueType +{ + Q_PROPERTY(qreal scalar READ scalar WRITE setScalar) + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_OBJECT +public: + QQmlQuaternionValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal scalar() const; + qreal x() const; + qreal y() const; + qreal z() const; + void setScalar(qreal); + void setX(qreal); + void setY(qreal); + void setZ(qreal); + +private: + QQuaternion quaternion; +}; + +class Q_QML_PRIVATE_EXPORT QQmlMatrix4x4ValueType : public QQmlValueType +{ + Q_PROPERTY(qreal m11 READ m11 WRITE setM11) + Q_PROPERTY(qreal m12 READ m12 WRITE setM12) + Q_PROPERTY(qreal m13 READ m13 WRITE setM13) + Q_PROPERTY(qreal m14 READ m14 WRITE setM14) + Q_PROPERTY(qreal m21 READ m21 WRITE setM21) + Q_PROPERTY(qreal m22 READ m22 WRITE setM22) + Q_PROPERTY(qreal m23 READ m23 WRITE setM23) + Q_PROPERTY(qreal m24 READ m24 WRITE setM24) + Q_PROPERTY(qreal m31 READ m31 WRITE setM31) + Q_PROPERTY(qreal m32 READ m32 WRITE setM32) + Q_PROPERTY(qreal m33 READ m33 WRITE setM33) + Q_PROPERTY(qreal m34 READ m34 WRITE setM34) + Q_PROPERTY(qreal m41 READ m41 WRITE setM41) + Q_PROPERTY(qreal m42 READ m42 WRITE setM42) + Q_PROPERTY(qreal m43 READ m43 WRITE setM43) + Q_PROPERTY(qreal m44 READ m44 WRITE setM44) + Q_OBJECT +public: + QQmlMatrix4x4ValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal m11() const { return matrix(0, 0); } + qreal m12() const { return matrix(0, 1); } + qreal m13() const { return matrix(0, 2); } + qreal m14() const { return matrix(0, 3); } + qreal m21() const { return matrix(1, 0); } + qreal m22() const { return matrix(1, 1); } + qreal m23() const { return matrix(1, 2); } + qreal m24() const { return matrix(1, 3); } + qreal m31() const { return matrix(2, 0); } + qreal m32() const { return matrix(2, 1); } + qreal m33() const { return matrix(2, 2); } + qreal m34() const { return matrix(2, 3); } + qreal m41() const { return matrix(3, 0); } + qreal m42() const { return matrix(3, 1); } + qreal m43() const { return matrix(3, 2); } + qreal m44() const { return matrix(3, 3); } + + void setM11(qreal value) { matrix(0, 0) = value; } + void setM12(qreal value) { matrix(0, 1) = value; } + void setM13(qreal value) { matrix(0, 2) = value; } + void setM14(qreal value) { matrix(0, 3) = value; } + void setM21(qreal value) { matrix(1, 0) = value; } + void setM22(qreal value) { matrix(1, 1) = value; } + void setM23(qreal value) { matrix(1, 2) = value; } + void setM24(qreal value) { matrix(1, 3) = value; } + void setM31(qreal value) { matrix(2, 0) = value; } + void setM32(qreal value) { matrix(2, 1) = value; } + void setM33(qreal value) { matrix(2, 2) = value; } + void setM34(qreal value) { matrix(2, 3) = value; } + void setM41(qreal value) { matrix(3, 0) = value; } + void setM42(qreal value) { matrix(3, 1) = value; } + void setM43(qreal value) { matrix(3, 2) = value; } + void setM44(qreal value) { matrix(3, 3) = value; } + +private: + QMatrix4x4 matrix; +}; + +class Q_QML_PRIVATE_EXPORT QQmlEasingValueType : public QQmlValueType +{ + Q_OBJECT + Q_ENUMS(Type) + + Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType) + Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude) + Q_PROPERTY(qreal overshoot READ overshoot WRITE setOvershoot) + Q_PROPERTY(qreal period READ period WRITE setPeriod) + Q_PROPERTY(QVariantList bezierCurve READ bezierCurve WRITE setBezierCurve) +public: + enum Type { + Linear = QEasingCurve::Linear, + InQuad = QEasingCurve::InQuad, OutQuad = QEasingCurve::OutQuad, + InOutQuad = QEasingCurve::InOutQuad, OutInQuad = QEasingCurve::OutInQuad, + InCubic = QEasingCurve::InCubic, OutCubic = QEasingCurve::OutCubic, + InOutCubic = QEasingCurve::InOutCubic, OutInCubic = QEasingCurve::OutInCubic, + InQuart = QEasingCurve::InQuart, OutQuart = QEasingCurve::OutQuart, + InOutQuart = QEasingCurve::InOutQuart, OutInQuart = QEasingCurve::OutInQuart, + InQuint = QEasingCurve::InQuint, OutQuint = QEasingCurve::OutQuint, + InOutQuint = QEasingCurve::InOutQuint, OutInQuint = QEasingCurve::OutInQuint, + InSine = QEasingCurve::InSine, OutSine = QEasingCurve::OutSine, + InOutSine = QEasingCurve::InOutSine, OutInSine = QEasingCurve::OutInSine, + InExpo = QEasingCurve::InExpo, OutExpo = QEasingCurve::OutExpo, + InOutExpo = QEasingCurve::InOutExpo, OutInExpo = QEasingCurve::OutInExpo, + InCirc = QEasingCurve::InCirc, OutCirc = QEasingCurve::OutCirc, + InOutCirc = QEasingCurve::InOutCirc, OutInCirc = QEasingCurve::OutInCirc, + InElastic = QEasingCurve::InElastic, OutElastic = QEasingCurve::OutElastic, + InOutElastic = QEasingCurve::InOutElastic, OutInElastic = QEasingCurve::OutInElastic, + InBack = QEasingCurve::InBack, OutBack = QEasingCurve::OutBack, + InOutBack = QEasingCurve::InOutBack, OutInBack = QEasingCurve::OutInBack, + InBounce = QEasingCurve::InBounce, OutBounce = QEasingCurve::OutBounce, + InOutBounce = QEasingCurve::InOutBounce, OutInBounce = QEasingCurve::OutInBounce, + InCurve = QEasingCurve::InCurve, OutCurve = QEasingCurve::OutCurve, + SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve, + Bezier = QEasingCurve::BezierSpline + }; + + QQmlEasingValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + Type type() const; + qreal amplitude() const; + qreal overshoot() const; + qreal period() const; + void setType(Type); + void setAmplitude(qreal); + void setOvershoot(qreal); + void setPeriod(qreal); + void setBezierCurve(const QVariantList &); + QVariantList bezierCurve() const; + + +private: + QEasingCurve easing; +}; + +class Q_QML_PRIVATE_EXPORT QQmlFontValueType : public QQmlValueType +{ + Q_OBJECT + Q_ENUMS(FontWeight) + Q_ENUMS(Capitalization) + + Q_PROPERTY(QString family READ family WRITE setFamily) + Q_PROPERTY(bool bold READ bold WRITE setBold) + Q_PROPERTY(FontWeight weight READ weight WRITE setWeight) + Q_PROPERTY(bool italic READ italic WRITE setItalic) + Q_PROPERTY(bool underline READ underline WRITE setUnderline) + Q_PROPERTY(bool overline READ overline WRITE setOverline) + Q_PROPERTY(bool strikeout READ strikeout WRITE setStrikeout) + Q_PROPERTY(qreal pointSize READ pointSize WRITE setPointSize) + Q_PROPERTY(int pixelSize READ pixelSize WRITE setPixelSize) + Q_PROPERTY(Capitalization capitalization READ capitalization WRITE setCapitalization) + Q_PROPERTY(qreal letterSpacing READ letterSpacing WRITE setLetterSpacing) + Q_PROPERTY(qreal wordSpacing READ wordSpacing WRITE setWordSpacing) + +public: + enum FontWeight { Light = QFont::Light, + Normal = QFont::Normal, + DemiBold = QFont::DemiBold, + Bold = QFont::Bold, + Black = QFont::Black }; + enum Capitalization { MixedCase = QFont::MixedCase, + AllUppercase = QFont::AllUppercase, + AllLowercase = QFont::AllLowercase, + SmallCaps = QFont::SmallCaps, + Capitalize = QFont::Capitalize }; + + QQmlFontValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + QString family() const; + void setFamily(const QString &); + + bool bold() const; + void setBold(bool b); + + FontWeight weight() const; + void setWeight(FontWeight); + + bool italic() const; + void setItalic(bool b); + + bool underline() const; + void setUnderline(bool b); + + bool overline() const; + void setOverline(bool b); + + bool strikeout() const; + void setStrikeout(bool b); + + qreal pointSize() const; + void setPointSize(qreal size); + + int pixelSize() const; + void setPixelSize(int size); + + Capitalization capitalization() const; + void setCapitalization(Capitalization); + + qreal letterSpacing() const; + void setLetterSpacing(qreal spacing); + + qreal wordSpacing() const; + void setWordSpacing(qreal spacing); + + void onLoad(); +private: + QFont font; + bool pixelSizeSet; + bool pointSizeSet; + mutable QQmlNullableValue<int> dpi; +}; + +class Q_QML_PRIVATE_EXPORT QQmlColorValueType : public QQmlValueType +{ + Q_PROPERTY(qreal r READ r WRITE setR) + Q_PROPERTY(qreal g READ g WRITE setG) + Q_PROPERTY(qreal b READ b WRITE setB) + Q_PROPERTY(qreal a READ a WRITE setA) + Q_OBJECT +public: + QQmlColorValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QQmlPropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(const QVariant &value); + virtual QString toString() const; + virtual bool isEqual(const QVariant &value) const; + + qreal r() const; + qreal g() const; + qreal b() const; + qreal a() const; + void setR(qreal); + void setG(qreal); + void setB(qreal); + void setA(qreal); + +private: + QColor color; +}; + +void QQmlValueType::onLoad() +{ +} + +QT_END_NAMESPACE + +#endif // QQMLVALUETYPE_P_H diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp new file mode 100644 index 0000000000..2b66e00bad --- /dev/null +++ b/src/qml/qml/qqmlvme.cpp @@ -0,0 +1,1370 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlvme_p.h" + +#include "qqmlcompiler_p.h" +#include "qqmlboundsignal_p.h" +#include "qqmlstringconverters_p.h" +#include <private/qmetaobjectbuilder_p.h> +#include <private/qfastmetabuilder_p.h> +#include "qqmldata_p.h" +#include "qqml.h" +#include "qqmlcustomparser_p.h" +#include "qqmlengine.h" +#include "qqmlcontext.h" +#include "qqmlcomponent.h" +#include "qqmlcomponentattached_p.h" +#include "qqmlbinding_p.h" +#include "qqmlengine_p.h" +#include "qqmlcomponent_p.h" +#include "qqmlvmemetaobject_p.h" +#include "qqmlbinding_p_p.h" +#include "qqmlcontext_p.h" +#include <private/qv4bindings_p.h> +#include <private/qv8bindings_p.h> +#include "qqmlglobal_p.h" +#include <private/qfinitestack_p.h> +#include "qqmlscriptstring.h" +#include "qqmlscriptstring_p.h" +#include "qqmlpropertyvalueinterceptor_p.h" + +#include <QStack> +#include <QColor> +#include <QPointF> +#include <QSizeF> +#include <QRectF> +#include <QtCore/qdebug.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qvarlengtharray.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_NAMESPACE + +using namespace QQmlVMETypes; + +#define VME_EXCEPTION(desc, line) \ + { \ + QQmlError error; \ + error.setDescription(desc.trimmed()); \ + error.setLine(line); \ + error.setUrl(COMP->url); \ + *errors << error; \ + goto exceptionExit; \ + } + +void QQmlVME::init(QQmlContextData *ctxt, QQmlCompiledData *comp, int start, + QQmlContextData *creation) +{ + Q_ASSERT(ctxt); + Q_ASSERT(comp); + + if (start == -1) { + start = 0; + } else { + creationContext = creation; + } + + State initState; + initState.context = ctxt; + initState.compiledData = comp; + initState.instructionStream = comp->bytecode.constData() + start; + states.push(initState); + + typedef QQmlInstruction I; + I *i = (I *)initState.instructionStream; + + Q_ASSERT(comp->instructionType(i) == I::Init); + + objects.allocate(i->init.objectStackSize); + lists.allocate(i->init.listStackSize); + bindValues.allocate(i->init.bindingsSize); + parserStatus.allocate(i->init.parserStatusSize); + +#ifdef QML_ENABLE_TRACE + parserStatusData.allocate(i->init.parserStatusSize); + rootComponent = comp; +#endif + + rootContext = 0; + engine = ctxt->engine; +} + +bool QQmlVME::initDeferred(QObject *object) +{ + QQmlData *data = QQmlData::get(object); + + if (!data || !data->context || !data->deferredComponent) + return false; + + QQmlContextData *ctxt = data->context; + QQmlCompiledData *comp = data->deferredComponent; + int start = data->deferredIdx; + + State initState; + initState.flags = State::Deferred; + initState.context = ctxt; + initState.compiledData = comp; + initState.instructionStream = comp->bytecode.constData() + start; + states.push(initState); + + typedef QQmlInstruction I; + I *i = (I *)initState.instructionStream; + + Q_ASSERT(comp->instructionType(i) == I::DeferInit); + + objects.allocate(i->deferInit.objectStackSize); + lists.allocate(i->deferInit.listStackSize); + bindValues.allocate(i->deferInit.bindingsSize); + parserStatus.allocate(i->deferInit.parserStatusSize); + + objects.push(object); + +#ifdef QML_ENABLE_TRACE + parserStatusData.allocate(i->deferInit.parserStatusSize); + rootComponent = comp; +#endif + + rootContext = 0; + engine = ctxt->engine; + + return true; +} + +namespace { +struct ActiveVMERestorer +{ + ActiveVMERestorer(QQmlVME *me, QQmlEnginePrivate *ep) + : ep(ep), oldVME(ep->activeVME) { ep->activeVME = me; } + ~ActiveVMERestorer() { ep->activeVME = oldVME; } + + QQmlEnginePrivate *ep; + QQmlVME *oldVME; +}; +} + +QObject *QQmlVME::execute(QList<QQmlError> *errors, const Interrupt &interrupt) +{ + Q_ASSERT(states.count() >= 1); + +#ifdef QML_ENABLE_TRACE + QQmlTrace trace("VME Execute"); + trace.addDetail("URL", rootComponent->url); +#endif + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(states.at(0).context->engine); + + ActiveVMERestorer restore(this, ep); + + QObject *rv = run(errors, interrupt); + + return rv; +} + +inline bool fastHasBinding(QObject *o, int index) +{ + QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(o)->declarativeData); + + return ddata && (ddata->bindingBitsSize > index) && + (ddata->bindingBits[index / 32] & (1 << (index % 32))); +} + +static void removeBindingOnProperty(QObject *o, int index) +{ + QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, index, -1, 0); + if (binding) binding->destroy(); +} + +static QVariant variantFromString(const QString &string) +{ + return QQmlStringConverters::variantFromString(string); +} + +// XXX we probably need some form of "work count" here to prevent us checking this +// for every instruction. +#define QML_BEGIN_INSTR_COMMON(I) { \ + const QQmlInstructionMeta<(int)QQmlInstruction::I>::DataType &instr = QQmlInstructionMeta<(int)QQmlInstruction::I>::data(*genericInstr); \ + INSTRUCTIONSTREAM += QQmlInstructionMeta<(int)QQmlInstruction::I>::Size; \ + Q_UNUSED(instr); + +#ifdef QML_THREADED_VME_INTERPRETER +# define QML_BEGIN_INSTR(I) op_##I: \ + QML_BEGIN_INSTR_COMMON(I) + +# define QML_NEXT_INSTR(I) { \ + if (watcher.hasRecursed()) return 0; \ + genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \ + goto *genericInstr->common.code; \ + } + +# define QML_END_INSTR(I) } \ + if (watcher.hasRecursed()) return 0; \ + genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \ + if (interrupt.shouldInterrupt()) return 0; \ + goto *genericInstr->common.code; + +#else +# define QML_BEGIN_INSTR(I) \ + case QQmlInstruction::I: \ + QML_BEGIN_INSTR_COMMON(I) + +# define QML_NEXT_INSTR(I) { \ + if (watcher.hasRecursed()) return 0; \ + break; \ + } + +# define QML_END_INSTR(I) \ + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return 0; \ + } break; +#endif + +#define QML_STORE_VALUE(name, cpptype, value) \ + QML_BEGIN_INSTR(name) \ + cpptype v = value; \ + void *a[] = { (void *)&v, 0, &status, &flags }; \ + QObject *target = objects.top(); \ + CLEAN_PROPERTY(target, instr.propertyIndex); \ + QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \ + QML_END_INSTR(name) + +#define QML_STORE_LIST(name, cpptype, value) \ + QML_BEGIN_INSTR(name) \ + cpptype v; \ + v.append(value); \ + void *a[] = { (void *)&v, 0, &status, &flags }; \ + QObject *target = objects.top(); \ + CLEAN_PROPERTY(target, instr.propertyIndex); \ + QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \ + QML_END_INSTR(name) + +#define QML_STORE_VAR(name, value) \ + QML_BEGIN_INSTR(name) \ + v8::Handle<v8::Value> v8value = value; \ + QObject *target = objects.top(); \ + CLEAN_PROPERTY(target, instr.propertyIndex); \ + QMetaObject *mo = const_cast<QMetaObject *>(target->metaObject()); \ + QQmlVMEMetaObject *vmemo = static_cast<QQmlVMEMetaObject *>(mo); \ + vmemo->setVMEProperty(instr.propertyIndex, v8value); \ + QML_END_INSTR(name) + +#define QML_STORE_POINTER(name, value) \ + QML_BEGIN_INSTR(name) \ + void *a[] = { (void *)value, 0, &status, &flags }; \ + QObject *target = objects.top(); \ + CLEAN_PROPERTY(target, instr.propertyIndex); \ + QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \ + QML_END_INSTR(name) + +#define CLEAN_PROPERTY(o, index) \ + if (fastHasBinding(o, index)) \ + removeBindingOnProperty(o, index) + +QObject *QQmlVME::run(QList<QQmlError> *errors, + const Interrupt &interrupt +#ifdef QML_THREADED_VME_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef QML_THREADED_VME_INTERPRETER + if (storeJumpTable) { +#define QML_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_QML_INSTR(QML_INSTR_ADDR) + }; +#undef QML_INSTR_ADDR + *storeJumpTable = jumpTable; + return 0; + } +#endif + Q_ASSERT(errors->isEmpty()); + Q_ASSERT(states.count() >= 1); + + QQmlEngine *engine = states.at(0).context->engine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + + // Need a v8 handle scope and execution context for StoreVar instructions. + v8::HandleScope handleScope; + v8::Context::Scope contextScope(ep->v8engine()->context()); + + int status = -1; // needed for dbus + QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + + QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this); + +#define COMP states.top().compiledData +#define CTXT states.top().context +#define INSTRUCTIONSTREAM states.top().instructionStream +#define BINDINGSKIPLIST states.top().bindingSkipList + +#define TYPES COMP->types +#define PRIMITIVES COMP->primitives +#define DATAS COMP->datas +#define PROGRAMS COMP->programs +#define PROPERTYCACHES COMP->propertyCaches +#define SCRIPTS COMP->scripts +#define URLS COMP->urls + +#ifdef QML_THREADED_VME_INTERPRETER + const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); + goto *genericInstr->common.code; +#else + for (;;) { + const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); + + switch (genericInstr->common.instructionType) { +#endif + + // Store a created object in a property. These all pop from the objects stack. + QML_STORE_VALUE(StoreObject, QObject *, objects.pop()); + QML_STORE_VALUE(StoreVariantObject, QVariant, QVariant::fromValue(objects.pop())); + QML_STORE_VAR(StoreVarObject, ep->v8engine()->newQObject(objects.pop())); + + // Store a literal value in a corresponding property + QML_STORE_VALUE(StoreFloat, float, instr.value); + QML_STORE_VALUE(StoreDouble, double, instr.value); + QML_STORE_VALUE(StoreBool, bool, instr.value); + QML_STORE_VALUE(StoreInteger, int, instr.value); + QML_STORE_VALUE(StoreColor, QColor, QColor::fromRgba(instr.value)); + QML_STORE_VALUE(StoreDate, QDate, QDate::fromJulianDay(instr.value)); + QML_STORE_VALUE(StoreDateTime, QDateTime, + QDateTime(QDate::fromJulianDay(instr.date), *(QTime *)&instr.time)); + QML_STORE_POINTER(StoreTime, (QTime *)&instr.time); + QML_STORE_POINTER(StorePoint, (QPoint *)&instr.point); + QML_STORE_POINTER(StorePointF, (QPointF *)&instr.point); + QML_STORE_POINTER(StoreSize, (QSize *)&instr.size); + QML_STORE_POINTER(StoreSizeF, (QSizeF *)&instr.size); + QML_STORE_POINTER(StoreRect, (QRect *)&instr.rect); + QML_STORE_POINTER(StoreRectF, (QRectF *)&instr.rect); + QML_STORE_POINTER(StoreVector3D, (QVector3D *)&instr.vector); + QML_STORE_POINTER(StoreVector4D, (QVector4D *)&instr.vector); + QML_STORE_POINTER(StoreString, &PRIMITIVES.at(instr.value)); + QML_STORE_POINTER(StoreByteArray, &DATAS.at(instr.value)); + QML_STORE_POINTER(StoreUrl, &URLS.at(instr.value)); + QML_STORE_VALUE(StoreTrString, QString, + QCoreApplication::translate(DATAS.at(instr.context).constData(), + DATAS.at(instr.text).constData(), + DATAS.at(instr.comment).constData(), + QCoreApplication::UnicodeUTF8, + instr.n)); + QML_STORE_VALUE(StoreTrIdString, QString, qtTrId(DATAS.at(instr.text).constData(), instr.n)); + + // Store a literal value in a QList + QML_STORE_LIST(StoreStringList, QStringList, PRIMITIVES.at(instr.value)); + QML_STORE_LIST(StoreStringQList, QList<QString>, PRIMITIVES.at(instr.value)); + QML_STORE_LIST(StoreUrlQList, QList<QUrl>, URLS.at(instr.value)); + QML_STORE_LIST(StoreDoubleQList, QList<double>, instr.value); + QML_STORE_LIST(StoreBoolQList, QList<bool>, instr.value); + QML_STORE_LIST(StoreIntegerQList, QList<int>, instr.value); + + // Store a literal value in a QVariant property + QML_STORE_VALUE(StoreVariant, QVariant, variantFromString(PRIMITIVES.at(instr.value))); + QML_STORE_VALUE(StoreVariantInteger, QVariant, QVariant(instr.value)); + QML_STORE_VALUE(StoreVariantDouble, QVariant, QVariant(instr.value)); + QML_STORE_VALUE(StoreVariantBool, QVariant, QVariant(instr.value)); + + // Store a literal value in a var property. + // We deliberately do not use string converters here + QML_STORE_VAR(StoreVar, ep->v8engine()->fromVariant(PRIMITIVES.at(instr.value))); + QML_STORE_VAR(StoreVarInteger, v8::Integer::New(instr.value)); + QML_STORE_VAR(StoreVarDouble, v8::Number::New(instr.value)); + QML_STORE_VAR(StoreVarBool, v8::Boolean::New(instr.value)); + + + QML_BEGIN_INSTR(Init) + // Ensure that the compiled data has been initialized + if (!COMP->isInitialized()) COMP->initialize(engine); + + QQmlContextData *parentCtxt = CTXT; + CTXT = new QQmlContextData; + CTXT->isInternal = true; + CTXT->url = COMP->url; + CTXT->urlString = COMP->name; + CTXT->imports = COMP->importCache; + CTXT->imports->addref(); + CTXT->setParent(parentCtxt); + if (instr.contextCache != -1) + CTXT->setIdPropertyData(COMP->contextCaches.at(instr.contextCache)); + if (instr.compiledBinding != -1) { + const char *v4data = DATAS.at(instr.compiledBinding).constData(); + CTXT->v4bindings = new QV4Bindings(v4data, CTXT, COMP); + } + if (states.count() == 1) { + rootContext = CTXT; + rootContext->activeVMEData = data; + } + if (states.count() == 1 && !creationContext.isNull()) { + // A component that is logically created within another component instance shares the + // same instances of script imports. For example: + // + // import QtQuick 2.0 + // import "test.js" as Test + // ListView { + // model: Test.getModel() + // delegate: Component { + // Text { text: Test.getValue(index); } + // } + // } + // + // Has the same "Test" instance. To implement this, we simply copy the v8 handles into + // the inner context. We have to create a fresh persistent handle for each to prevent + // double dispose. It is possible we could do this more efficiently using some form of + // referencing instead. + CTXT->importedScripts = creationContext->importedScripts; + for (int ii = 0; ii < CTXT->importedScripts.count(); ++ii) + CTXT->importedScripts[ii] = qPersistentNew<v8::Object>(CTXT->importedScripts[ii]); + } + QML_END_INSTR(Init) + + QML_BEGIN_INSTR(DeferInit) + QML_END_INSTR(DeferInit) + + QML_BEGIN_INSTR(Done) + states.pop(); + + if (states.isEmpty()) + goto normalExit; + QML_END_INSTR(Done) + + QML_BEGIN_INSTR(CreateQMLObject) + const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type); + Q_ASSERT(type.component); + + states.push(State()); + + State *cState = &states[states.count() - 2]; + State *nState = &states[states.count() - 1]; + + nState->context = cState->context; + nState->compiledData = type.component; + nState->instructionStream = type.component->bytecode.constData(); + + if (instr.bindingBits != -1) { + const QByteArray &bits = cState->compiledData->datas.at(instr.bindingBits); + nState->bindingSkipList = QBitField((const quint32*)bits.constData(), + bits.size() * 8); + } + if (instr.isRoot) + nState->bindingSkipList = nState->bindingSkipList.united(cState->bindingSkipList); + + // As the state in the state stack changed, execution will continue in the new program. + QML_END_INSTR(CreateQMLObject) + + QML_BEGIN_INSTR(CompleteQMLObject) + QObject *o = objects.top(); + + QQmlData *ddata = QQmlData::get(o); + Q_ASSERT(ddata); + + if (instr.isRoot) { + if (ddata->context) { + Q_ASSERT(ddata->context != CTXT); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != CTXT); + QQmlContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = CTXT; + } else { + CTXT->addObject(o); + } + + ddata->ownContext = true; + } else if (!ddata->context) { + CTXT->addObject(o); + } + + ddata->setImplicitDestructible(); + ddata->outerContext = CTXT; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.column; + QML_END_INSTR(CompleteQMLObject) + + QML_BEGIN_INSTR(CreateCppObject) + const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type); + Q_ASSERT(type.type); + + QObject *o = 0; + void *memory = 0; + type.type->create(&o, &memory, sizeof(QQmlData)); + QQmlData *ddata = new (memory) QQmlData; + ddata->ownMemory = false; + QObjectPrivate::get(o)->declarativeData = ddata; + + if (type.typePropertyCache && !ddata->propertyCache) { + ddata->propertyCache = type.typePropertyCache; + ddata->propertyCache->addref(); + } + + if (!o) + VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.className), instr.line); + + if (instr.isRoot) { + if (ddata->context) { + Q_ASSERT(ddata->context != CTXT); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != CTXT); + QQmlContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = CTXT; + } else { + CTXT->addObject(o); + } + + ddata->ownContext = true; + } else if (!ddata->context) { + CTXT->addObject(o); + } + + ddata->setImplicitDestructible(); + ddata->outerContext = CTXT; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.column; + + if (instr.data != -1) { + QQmlCustomParser *customParser = + TYPES.at(instr.type).type->customParser(); + customParser->setCustomData(o, DATAS.at(instr.data)); + } + if (!objects.isEmpty()) { + QObject *parent = objects.top(); +#if 0 // ### refactor + if (o->isWidgetType() && parent->isWidgetType()) + static_cast<QWidget*>(o)->setParent(static_cast<QWidget*>(parent)); + else +#endif + QQml_setParent_noEvent(o, parent); + } + objects.push(o); + QML_END_INSTR(CreateCppObject) + + QML_BEGIN_INSTR(CreateSimpleObject) + QObject *o = (QObject *)operator new(instr.typeSize + sizeof(QQmlData)); + ::memset(o, 0, instr.typeSize + sizeof(QQmlData)); + instr.create(o); + + QQmlData *ddata = (QQmlData *)(((const char *)o) + instr.typeSize); + const QQmlCompiledData::TypeReference &ref = TYPES.at(instr.type); + if (!ddata->propertyCache && ref.typePropertyCache) { + ddata->propertyCache = ref.typePropertyCache; + ddata->propertyCache->addref(); + } + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.column; + + QObjectPrivate::get(o)->declarativeData = ddata; + ddata->context = ddata->outerContext = CTXT; + ddata->nextContextObject = CTXT->contextObjects; + if (ddata->nextContextObject) + ddata->nextContextObject->prevContextObject = &ddata->nextContextObject; + ddata->prevContextObject = &CTXT->contextObjects; + CTXT->contextObjects = ddata; + + QObject *parent = objects.top(); + QQml_setParent_noEvent(o, parent); + + objects.push(o); + QML_END_INSTR(CreateSimpleObject) + + QML_BEGIN_INSTR(SetId) + QObject *target = objects.top(); + CTXT->setIdProperty(instr.index, target); + QML_END_INSTR(SetId) + + QML_BEGIN_INSTR(SetDefault) + CTXT->contextObject = objects.top(); + QML_END_INSTR(SetDefault) + + QML_BEGIN_INSTR(CreateComponent) + QQmlComponent *qcomp = + new QQmlComponent(CTXT->engine, COMP, INSTRUCTIONSTREAM - COMP->bytecode.constData(), + objects.isEmpty() ? 0 : objects.top()); + + QQmlData *ddata = QQmlData::get(qcomp, true); + Q_ASSERT(ddata); + + CTXT->addObject(qcomp); + + if (instr.isRoot) + ddata->ownContext = true; + + ddata->setImplicitDestructible(); + ddata->outerContext = CTXT; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.column; + + QQmlComponentPrivate::get(qcomp)->creationContext = CTXT; + + objects.push(qcomp); + INSTRUCTIONSTREAM += instr.count; + QML_END_INSTR(CreateComponent) + + QML_BEGIN_INSTR(StoreMetaObject) + QObject *target = objects.top(); + + QMetaObject mo; + const QByteArray &metadata = DATAS.at(instr.data); + QFastMetaBuilder::fromData(&mo, 0, metadata); + + const QQmlVMEMetaData *data = + (const QQmlVMEMetaData *)DATAS.at(instr.aliasData).constData(); + + (void)new QQmlVMEMetaObject(target, &mo, data, COMP); + + if (instr.propertyCache != -1) { + QQmlData *ddata = QQmlData::get(target, true); + if (ddata->propertyCache) ddata->propertyCache->release(); + ddata->propertyCache = PROPERTYCACHES.at(instr.propertyCache); + ddata->propertyCache->addref(); + } + QML_END_INSTR(StoreMetaObject) + + QML_BEGIN_INSTR(AssignCustomType) + QObject *target = objects.top(); + CLEAN_PROPERTY(target, instr.propertyIndex); + + const QString &primitive = PRIMITIVES.at(instr.primitive); + int type = instr.type; + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type); + QVariant v = (*converter)(primitive); + + QMetaProperty prop = + target->metaObject()->property(instr.propertyIndex); + if (v.isNull() || ((int)prop.type() != type && prop.userType() != type)) + VME_EXCEPTION(tr("Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name())), instr.line); + + void *a[] = { (void *)v.data(), 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.propertyIndex, a); + QML_END_INSTR(AssignCustomType) + + QML_BEGIN_INSTR(AssignSignalObject) + // XXX optimize + + QObject *assign = objects.pop(); + QObject *target = objects.top(); + int sigIdx = instr.signal; + const QString &pr = PRIMITIVES.at(sigIdx); + + QQmlProperty prop(target, pr); + if (prop.type() & QQmlProperty::SignalProperty) { + + QMetaMethod method = QQmlMetaType::defaultMethod(assign); + if (method.signature() == 0) + VME_EXCEPTION(tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className())), instr.line); + + if (!QMetaObject::checkConnectArgs(prop.method().signature(), method.signature())) + VME_EXCEPTION(tr("Cannot connect mismatched signal/slot %1 %vs. %2").arg(QString::fromLatin1(method.signature())).arg(QString::fromLatin1(prop.method().signature())), instr.line); + + QQmlPropertyPrivate::connect(target, prop.index(), assign, method.methodIndex()); + + } else { + VME_EXCEPTION(tr("Cannot assign an object to signal property %1").arg(pr), instr.line); + } + + + QML_END_INSTR(AssignSignalObject) + + QML_BEGIN_INSTR(StoreSignal) + QObject *target = objects.top(); + QObject *context = objects.at(objects.count() - 1 - instr.context); + + QMetaMethod signal = target->metaObject()->method(instr.signalIndex); + + QQmlBoundSignal *bs = new QQmlBoundSignal(target, signal, target); + QQmlExpression *expr = + new QQmlExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column, *new QQmlExpressionPrivate); + bs->setExpression(expr); + QML_END_INSTR(StoreSignal) + + QML_BEGIN_INSTR(StoreImportedScript) + CTXT->importedScripts << run(CTXT, SCRIPTS.at(instr.value)); + QML_END_INSTR(StoreImportedScript) + + QML_BEGIN_INSTR(StoreScriptString) + QObject *target = objects.top(); + QObject *scope = objects.at(objects.count() - 1 - instr.scope); + QQmlScriptString ss; + ss.setContext(CTXT->asQQmlContext()); + ss.setScopeObject(scope); + ss.setScript(PRIMITIVES.at(instr.value)); + ss.d.data()->bindingId = instr.bindingId; + ss.d.data()->lineNumber = instr.line; + ss.d.data()->columnNumber = instr.column; + + void *a[] = { &ss, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.propertyIndex, a); + QML_END_INSTR(StoreScriptString) + + QML_BEGIN_INSTR(BeginObject) + QObject *target = objects.top(); + QQmlParserStatus *status = reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(target) + instr.castValue); + parserStatus.push(status); +#ifdef QML_ENABLE_TRACE + Q_ASSERT(QObjectPrivate::get(target)->declarativeData); + parserStatusData.push(static_cast<QQmlData *>(QObjectPrivate::get(target)->declarativeData)); +#endif + status->d = &parserStatus.top(); + + status->classBegin(); + QML_END_INSTR(BeginObject) + + QML_BEGIN_INSTR(InitV8Bindings) + CTXT->v8bindings = new QV8Bindings(&PROGRAMS[instr.programIndex], instr.line, CTXT); + QML_END_INSTR(InitV8Bindings) + + QML_BEGIN_INSTR(StoreBinding) + QObject *target = + objects.at(objects.count() - 1 - instr.owner); + QObject *context = + objects.at(objects.count() - 1 - instr.context); + + if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex)) + QML_NEXT_INSTR(StoreBinding); + + QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true, + context, CTXT, COMP->name, instr.line, + instr.column); + bindValues.push(bind); + bind->m_mePtr = &bindValues.top(); + bind->setTarget(target, instr.property, CTXT); + + typedef QQmlPropertyPrivate QDPP; + Q_ASSERT(bind->propertyIndex() == QDPP::bindingIndex(instr.property)); + Q_ASSERT(bind->object() == target); + + bind->addToObject(); + QML_END_INSTR(StoreBinding) + + QML_BEGIN_INSTR(StoreBindingOnAlias) + QObject *target = + objects.at(objects.count() - 1 - instr.owner); + QObject *context = + objects.at(objects.count() - 1 - instr.context); + + if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex)) + QML_NEXT_INSTR(StoreBindingOnAlias); + + QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true, + context, CTXT, COMP->name, instr.line, + instr.column); + bindValues.push(bind); + bind->m_mePtr = &bindValues.top(); + bind->setTarget(target, instr.property, CTXT); + + QQmlAbstractBinding *old = + QQmlPropertyPrivate::setBindingNoEnable(target, instr.property.coreIndex, + instr.property.getValueTypeCoreIndex(), + bind); + if (old) { old->destroy(); } + QML_END_INSTR(StoreBindingOnAlias) + + QML_BEGIN_INSTR(StoreV4Binding) + QObject *target = + objects.at(objects.count() - 1 - instr.owner); + QObject *scope = + objects.at(objects.count() - 1 - instr.context); + + int property = instr.property; + if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF)) + QML_NEXT_INSTR(StoreV4Binding); + + QQmlAbstractBinding *binding = + CTXT->v4bindings->configBinding(instr.value, target, scope, property, + instr.line, instr.column); + bindValues.push(binding); + binding->m_mePtr = &bindValues.top(); + + Q_ASSERT(binding->propertyIndex() == property); + Q_ASSERT(binding->object() == target); + + binding->addToObject(); + QML_END_INSTR(StoreV4Binding) + + QML_BEGIN_INSTR(StoreV8Binding) + QObject *target = + objects.at(objects.count() - 1 - instr.owner); + QObject *scope = + objects.at(objects.count() - 1 - instr.context); + + if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex)) + QML_NEXT_INSTR(StoreV8Binding); + + QQmlAbstractBinding *binding = CTXT->v8bindings->configBinding(target, scope, + &instr); + if (binding) { + bindValues.push(binding); + binding->m_mePtr = &bindValues.top(); + + typedef QQmlPropertyPrivate QDPP; + Q_ASSERT(binding->propertyIndex() == QDPP::bindingIndex(instr.property)); + Q_ASSERT(binding->object() == target); + + binding->addToObject(); + } + QML_END_INSTR(StoreV8Binding) + + QML_BEGIN_INSTR(StoreValueSource) + QObject *obj = objects.pop(); + QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(obj) + instr.castValue); + QObject *target = objects.at(objects.count() - 1 - instr.owner); + + obj->setParent(target); + vs->setTarget(QQmlPropertyPrivate::restore(target, instr.property, CTXT)); + QML_END_INSTR(StoreValueSource) + + QML_BEGIN_INSTR(StoreValueInterceptor) + QObject *obj = objects.pop(); + QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(obj) + instr.castValue); + QObject *target = objects.at(objects.count() - 1 - instr.owner); + QQmlProperty prop = + QQmlPropertyPrivate::restore(target, instr.property, CTXT); + obj->setParent(target); + vi->setTarget(prop); + QQmlVMEMetaObject *mo = static_cast<QQmlVMEMetaObject *>((QMetaObject*)target->metaObject()); + mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi); + QML_END_INSTR(StoreValueInterceptor) + + QML_BEGIN_INSTR(StoreObjectQList) + QObject *assign = objects.pop(); + + const List &list = lists.top(); + list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, assign); + QML_END_INSTR(StoreObjectQList) + + QML_BEGIN_INSTR(AssignObjectList) + // This is only used for assigning interfaces + QObject *assign = objects.pop(); + const List &list = lists.top(); + + int type = list.type; + + void *ptr = 0; + + const char *iid = QQmlMetaType::interfaceIId(type); + if (iid) + ptr = assign->qt_metacast(iid); + if (!ptr) + VME_EXCEPTION(tr("Cannot assign object to list"), instr.line); + + + list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, ptr); + QML_END_INSTR(AssignObjectList) + + QML_BEGIN_INSTR(StoreInterface) + QObject *assign = objects.pop(); + QObject *target = objects.top(); + CLEAN_PROPERTY(target, instr.propertyIndex); + + int coreIdx = instr.propertyIndex; + QMetaProperty prop = target->metaObject()->property(coreIdx); + int t = prop.userType(); + const char *iid = QQmlMetaType::interfaceIId(t); + bool ok = false; + if (iid) { + void *ptr = assign->qt_metacast(iid); + if (ptr) { + void *a[] = { &ptr, 0, &status, &flags }; + QMetaObject::metacall(target, + QMetaObject::WriteProperty, + coreIdx, a); + ok = true; + } + } + + if (!ok) + VME_EXCEPTION(tr("Cannot assign object to interface property"), instr.line); + QML_END_INSTR(StoreInterface) + + QML_BEGIN_INSTR(FetchAttached) + QObject *target = objects.top(); + + QObject *qmlObject = qmlAttachedPropertiesObjectById(instr.id, target); + + if (!qmlObject) + VME_EXCEPTION(tr("Unable to create attached object"), instr.line); + + objects.push(qmlObject); + QML_END_INSTR(FetchAttached) + + QML_BEGIN_INSTR(FetchQList) + QObject *target = objects.top(); + + lists.push(List(instr.type)); + + void *a[1]; + a[0] = (void *)&(lists.top().qListProperty); + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.property, a); + QML_END_INSTR(FetchQList) + + QML_BEGIN_INSTR(FetchObject) + QObject *target = objects.top(); + + QObject *obj = 0; + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + void *a[1]; + a[0] = &obj; + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.property, a); + + if (!obj) + VME_EXCEPTION(tr("Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line); + + objects.push(obj); + QML_END_INSTR(FetchObject) + + QML_BEGIN_INSTR(PopQList) + lists.pop(); + QML_END_INSTR(PopQList) + + QML_BEGIN_INSTR(Defer) + if (instr.deferCount) { + QObject *target = objects.top(); + QQmlData *data = + QQmlData::get(target, true); + COMP->addref(); + data->deferredComponent = COMP; + data->deferredIdx = INSTRUCTIONSTREAM - COMP->bytecode.constData(); + INSTRUCTIONSTREAM += instr.deferCount; + } + QML_END_INSTR(Defer) + + QML_BEGIN_INSTR(PopFetchedObject) + objects.pop(); + QML_END_INSTR(PopFetchedObject) + + QML_BEGIN_INSTR(FetchValueType) + QObject *target = objects.top(); + + if (instr.bindingSkipList != 0) { + // Possibly need to clear bindings + QQmlData *targetData = QQmlData::get(target); + if (targetData) { + QQmlAbstractBinding *binding = + QQmlPropertyPrivate::binding(target, instr.property, -1); + + if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) { + QQmlPropertyPrivate::setBinding(target, instr.property, -1, 0); + binding->destroy(); + } else if (binding) { + QQmlValueTypeProxyBinding *proxy = + static_cast<QQmlValueTypeProxyBinding *>(binding); + proxy->removeBindings(instr.bindingSkipList); + } + } + } + + QQmlValueType *valueHandler = ep->valueTypes[instr.type]; + valueHandler->read(target, instr.property); + objects.push(valueHandler); + QML_END_INSTR(FetchValueType) + + QML_BEGIN_INSTR(PopValueType) + QQmlValueType *valueHandler = + static_cast<QQmlValueType *>(objects.pop()); + QObject *target = objects.top(); + valueHandler->write(target, instr.property, QQmlPropertyPrivate::BypassInterceptor); + QML_END_INSTR(PopValueType) + +#ifdef QML_THREADED_VME_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlCompiledData: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +exceptionExit: + Q_ASSERT(!states.isEmpty()); + Q_ASSERT(!errors->isEmpty()); + + reset(); + + return 0; + +normalExit: + Q_ASSERT(objects.count() == 1); + + QObject *rv = objects.top(); + + objects.deallocate(); + lists.deallocate(); + states.clear(); + + return rv; +} + +void QQmlVME::reset() +{ + Q_ASSERT(!states.isEmpty() || objects.isEmpty()); + + QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this); + + if (!objects.isEmpty() && !(states.at(0).flags & State::Deferred)) + delete objects.at(0); + + if (!rootContext.isNull()) + rootContext->activeVMEData = 0; + + // Remove the QQmlParserStatus and QQmlAbstractBinding back pointers + blank(parserStatus); + blank(bindValues); + + while (componentAttached) { + QQmlComponentAttached *a = componentAttached; + a->rem(); + } + + engine = 0; + objects.deallocate(); + lists.deallocate(); + bindValues.deallocate(); + parserStatus.deallocate(); +#ifdef QML_ENABLE_TRACE + parserStatusData.deallocate(); +#endif + finalizeCallbacks.clear(); + states.clear(); + rootContext = 0; + creationContext = 0; +} + +// Must be called with a handle scope and context +void QQmlScriptData::initialize(QQmlEngine *engine) +{ + Q_ASSERT(m_program.IsEmpty()); + Q_ASSERT(engine); + Q_ASSERT(!hasEngine()); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + QV8Engine *v8engine = ep->v8engine(); + + // If compilation throws an error, a surrounding v8::TryCatch will record it. + v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_programSource.constData(), + m_programSource.length(), urlString, 1); + if (program.IsEmpty()) + return; + + m_program = qPersistentNew<v8::Script>(program); + m_programSource.clear(); // We don't need this anymore + + addToEngine(engine); + + addref(); +} + +v8::Persistent<v8::Object> QQmlVME::run(QQmlContextData *parentCtxt, QQmlScriptData *script) +{ + if (script->m_loaded) + return qPersistentNew<v8::Object>(script->m_value); + + Q_ASSERT(parentCtxt && parentCtxt->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine); + QV8Engine *v8engine = ep->v8engine(); + + bool shared = script->pragmas & QQmlScript::Object::ScriptBlock::Shared; + + QQmlContextData *effectiveCtxt = parentCtxt; + if (shared) + effectiveCtxt = 0; + + // Create the script context if required + QQmlContextData *ctxt = new QQmlContextData; + ctxt->isInternal = true; + ctxt->isJSContext = true; + if (shared) + ctxt->isPragmaLibraryContext = true; + else + ctxt->isPragmaLibraryContext = parentCtxt->isPragmaLibraryContext; + ctxt->url = script->url; + ctxt->urlString = script->urlString; + + // For backward compatibility, if there are no imports, we need to use the + // imports from the parent context. See QTBUG-17518. + if (!script->importCache->isEmpty()) { + ctxt->imports = script->importCache; + } else if (effectiveCtxt) { + ctxt->imports = effectiveCtxt->imports; + ctxt->importedScripts = effectiveCtxt->importedScripts; + for (int ii = 0; ii < ctxt->importedScripts.count(); ++ii) + ctxt->importedScripts[ii] = qPersistentNew<v8::Object>(ctxt->importedScripts[ii]); + } + + if (ctxt->imports) { + ctxt->imports->addref(); + } + + if (effectiveCtxt) { + ctxt->setParent(effectiveCtxt, true); + } else { + ctxt->engine = parentCtxt->engine; // Fix for QTBUG-21620 + } + + for (int ii = 0; ii < script->scripts.count(); ++ii) { + ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData()); + } + + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + + v8::TryCatch try_catch; + if (!script->isInitialized()) + script->initialize(parentCtxt->engine); + + v8::Local<v8::Object> qmlglobal = v8engine->qmlScope(ctxt, 0); + + if (!script->m_program.IsEmpty()) { + script->m_program->Run(qmlglobal); + } else { + // Compilation failed. + Q_ASSERT(try_catch.HasCaught()); + } + + v8::Persistent<v8::Object> rv; + + if (try_catch.HasCaught()) { + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(message, error); + ep->warning(error); + } + } + + rv = qPersistentNew<v8::Object>(qmlglobal); + if (shared) { + script->m_value = qPersistentNew<v8::Object>(qmlglobal); + script->m_loaded = true; + } + + return rv; +} + +#ifdef QML_THREADED_VME_INTERPRETER +void **QQmlVME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + QQmlVME dummy; + QQmlVME::Interrupt i; + dummy.run(0, i, &jumpTable); + } + return jumpTable; +} +#endif + +QQmlContextData *QQmlVME::complete(const Interrupt &interrupt) +{ + Q_ASSERT(engine || + (bindValues.isEmpty() && + parserStatus.isEmpty() && + componentAttached == 0 && + rootContext.isNull() && + finalizeCallbacks.isEmpty())); + + if (!engine) + return 0; + + QQmlTrace trace("VME Complete"); +#ifdef QML_ENABLE_TRACE + trace.addDetail("URL", rootComponent->url); +#endif + + ActiveVMERestorer restore(this, QQmlEnginePrivate::get(engine)); + QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this); + + { + QQmlTrace trace("VME Binding Enable"); + trace.event("begin binding eval"); + while (!bindValues.isEmpty()) { + QQmlAbstractBinding *b = bindValues.pop(); + + if(b) { + b->m_mePtr = 0; + b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::DontRemoveBinding); + } + + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + return 0; + } + bindValues.deallocate(); + } + + { + QQmlTrace trace("VME Component Complete"); + while (!parserStatus.isEmpty()) { + QQmlParserStatus *status = parserStatus.pop(); +#ifdef QML_ENABLE_TRACE + QQmlData *data = parserStatusData.pop(); +#endif + + if (status && status->d) { + status->d = 0; +#ifdef QML_ENABLE_TRACE + QQmlTrace trace("Component complete"); + trace.addDetail("URL", data->outerContext->url); + trace.addDetail("Line", data->lineNumber); +#endif + status->componentComplete(); + } + + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + return 0; + } + parserStatus.deallocate(); + } + + { + QQmlTrace trace("VME Finalize Callbacks"); + for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) { + QQmlEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii); + QObject *obj = callback.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); + } + if (watcher.hasRecursed()) + return 0; + } + finalizeCallbacks.clear(); + } + + { + QQmlTrace trace("VME Component.onCompleted Callbacks"); + while (componentAttached) { + QQmlComponentAttached *a = componentAttached; + a->rem(); + QQmlData *d = QQmlData::get(a->parent()); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + emit a->completed(); + + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + return 0; + } + } + + QQmlContextData *rv = rootContext; + + reset(); + + if (rv) rv->activeVMEData = data; + + return rv; +} + +void QQmlVME::blank(QFiniteStack<QQmlAbstractBinding *> &bs) +{ + for (int ii = 0; ii < bs.count(); ++ii) { + QQmlAbstractBinding *b = bs.at(ii); + if (b) b->m_mePtr = 0; + } +} + +void QQmlVME::blank(QFiniteStack<QQmlParserStatus *> &pss) +{ + for (int ii = 0; ii < pss.count(); ++ii) { + QQmlParserStatus *ps = pss.at(ii); + if(ps) ps->d = 0; + } +} + +QQmlVMEGuard::QQmlVMEGuard() +: m_objectCount(0), m_objects(0), m_contextCount(0), m_contexts(0) +{ +} + +QQmlVMEGuard::~QQmlVMEGuard() +{ + clear(); +} + +void QQmlVMEGuard::guard(QQmlVME *vme) +{ + clear(); + + m_objectCount = vme->objects.count(); + m_objects = new QQmlGuard<QObject>[m_objectCount]; + for (int ii = 0; ii < m_objectCount; ++ii) + m_objects[ii] = vme->objects[ii]; + + m_contextCount = (vme->rootContext.isNull()?0:1) + vme->states.count(); + m_contexts = new QQmlGuardedContextData[m_contextCount]; + for (int ii = 0; ii < vme->states.count(); ++ii) + m_contexts[ii] = vme->states.at(ii).context; + if (!vme->rootContext.isNull()) + m_contexts[m_contextCount - 1] = vme->rootContext.contextData(); +} + +void QQmlVMEGuard::clear() +{ + delete [] m_objects; + delete [] m_contexts; + + m_objectCount = 0; + m_objects = 0; + m_contextCount = 0; + m_contexts = 0; +} + +bool QQmlVMEGuard::isOK() const +{ + for (int ii = 0; ii < m_objectCount; ++ii) + if (m_objects[ii].isNull()) + return false; + + for (int ii = 0; ii < m_contextCount; ++ii) + if (m_contexts[ii].isNull()) + return false; + + return true; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h new file mode 100644 index 0000000000..8c8d4d079e --- /dev/null +++ b/src/qml/qml/qqmlvme_p.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLVME_P_H +#define QQMLVME_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 "qqmlerror.h" +#include <private/qbitfield_p.h> +#include "qqmlinstruction_p.h" +#include <private/qrecursionwatcher_p.h> + +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qcoreapplication.h> + +#include <private/qv8_p.h> +#include <private/qqmlengine_p.h> +#include <private/qfinitestack_p.h> + +#include <private/qqmltrace_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QJSValue; +class QQmlScriptData; +class QQmlCompiledData; +class QQmlCompiledData; +class QQmlContextData; + +namespace QQmlVMETypes { + struct List + { + List() : type(0) {} + List(int t) : type(t) {} + + int type; + QQmlListProperty<void> qListProperty; + }; +} +Q_DECLARE_TYPEINFO(QQmlVMETypes::List, Q_PRIMITIVE_TYPE | Q_MOVABLE_TYPE); + +class QQmlVME +{ + Q_DECLARE_TR_FUNCTIONS(QQmlVME) +public: + class Interrupt { + public: + inline Interrupt(); + inline Interrupt(bool *runWhile); + inline Interrupt(int nsecs); + + inline void reset(); + inline bool shouldInterrupt() const; + private: + enum Mode { None, Time, Flag }; + Mode mode; + union { + struct { + QElapsedTimer timer; + int nsecs; + }; + bool *runWhile; + }; + }; + + QQmlVME() : data(0), componentAttached(0) {} + QQmlVME(void *data) : data(data), componentAttached(0) {} + + void *data; + QQmlComponentAttached *componentAttached; + QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; + + void init(QQmlContextData *, QQmlCompiledData *, int start, + QQmlContextData * = 0); + bool initDeferred(QObject *); + void reset(); + + QObject *execute(QList<QQmlError> *errors, const Interrupt & = Interrupt()); + QQmlContextData *complete(const Interrupt & = Interrupt()); + +private: + friend class QQmlVMEGuard; + + QObject *run(QList<QQmlError> *errors, const Interrupt & +#ifdef QML_THREADED_VME_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); + v8::Persistent<v8::Object> run(QQmlContextData *, QQmlScriptData *); + +#ifdef QML_THREADED_VME_INTERPRETER + static void **instructionJumpTable(); + friend class QQmlCompiledData; +#endif + + QQmlEngine *engine; + QRecursionNode recursion; + +#ifdef QML_ENABLE_TRACE + QQmlCompiledData *rootComponent; +#endif + + QFiniteStack<QObject *> objects; + QFiniteStack<QQmlVMETypes::List> lists; + + QFiniteStack<QQmlAbstractBinding *> bindValues; + QFiniteStack<QQmlParserStatus *> parserStatus; +#ifdef QML_ENABLE_TRACE + QFiniteStack<QQmlData *> parserStatusData; +#endif + + QQmlGuardedContextData rootContext; + QQmlGuardedContextData creationContext; + + struct State { + enum Flag { Deferred = 0x00000001 }; + + State() : flags(0), context(0), compiledData(0), instructionStream(0) {} + quint32 flags; + QQmlContextData *context; + QQmlCompiledData *compiledData; + const char *instructionStream; + QBitField bindingSkipList; + }; + + QStack<State> states; + + static void blank(QFiniteStack<QQmlParserStatus *> &); + static void blank(QFiniteStack<QQmlAbstractBinding *> &); +}; + +// Used to check that a QQmlVME that is interrupted mid-execution +// is still valid. Checks all the objects and contexts have not been +// deleted. +class QQmlVMEGuard +{ +public: + QQmlVMEGuard(); + ~QQmlVMEGuard(); + + void guard(QQmlVME *); + void clear(); + + bool isOK() const; + +private: + int m_objectCount; + QQmlGuard<QObject> *m_objects; + int m_contextCount; + QQmlGuardedContextData *m_contexts; +}; + +QQmlVME::Interrupt::Interrupt() +: mode(None) +{ +} + +QQmlVME::Interrupt::Interrupt(bool *runWhile) +: mode(Flag), runWhile(runWhile) +{ +} + +QQmlVME::Interrupt::Interrupt(int nsecs) +: mode(Time), nsecs(nsecs) +{ +} + +void QQmlVME::Interrupt::reset() +{ + if (mode == Time) + timer.start(); +} + +bool QQmlVME::Interrupt::shouldInterrupt() const +{ + if (mode == None) { + return false; + } else if (mode == Time) { + return timer.nsecsElapsed() > nsecs; + } else if (mode == Flag) { + return !*runWhile; + } else { + return false; + } +} + +QT_END_NAMESPACE + +#endif // QQMLVME_P_H diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp new file mode 100644 index 0000000000..7ea89a4a2d --- /dev/null +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -0,0 +1,1110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlvmemetaobject_p.h" + + +#include "qqml.h" +#include <private/qqmlrefcount_p.h> +#include "qqmlexpression.h" +#include "qqmlexpression_p.h" +#include "qqmlcontext_p.h" +#include "qqmlbinding_p.h" +#include "qqmlpropertyvalueinterceptor_p.h" + +#include <private/qv8variantresource_p.h> + +Q_DECLARE_METATYPE(QJSValue); + +QT_BEGIN_NAMESPACE + +class QQmlVMEVariant +{ +public: + inline QQmlVMEVariant(); + inline ~QQmlVMEVariant(); + + inline const void *dataPtr() const; + inline void *dataPtr(); + inline int dataType() const; + + inline QObject *asQObject(); + inline const QVariant &asQVariant(); + inline int asInt(); + inline bool asBool(); + inline double asDouble(); + inline const QString &asQString(); + inline const QUrl &asQUrl(); + inline const QColor &asQColor(); + inline const QTime &asQTime(); + inline const QDate &asQDate(); + inline const QDateTime &asQDateTime(); + inline const QJSValue &asQJSValue(); + + inline void setValue(QObject *); + inline void setValue(const QVariant &); + inline void setValue(int); + inline void setValue(bool); + inline void setValue(double); + inline void setValue(const QString &); + inline void setValue(const QUrl &); + inline void setValue(const QColor &); + inline void setValue(const QTime &); + inline void setValue(const QDate &); + inline void setValue(const QDateTime &); + inline void setValue(const QJSValue &); +private: + int type; + void *data[4]; // Large enough to hold all types + + inline void cleanup(); +}; + +class QQmlVMEMetaObjectEndpoint : public QQmlNotifierEndpoint +{ +public: + QQmlVMEMetaObjectEndpoint(); + static void vmecallback(QQmlNotifierEndpoint *); + void tryConnect(); + + QFlagPointer<QQmlVMEMetaObject> metaObject; +}; + + +QQmlVMEVariant::QQmlVMEVariant() +: type(QVariant::Invalid) +{ +} + +QQmlVMEVariant::~QQmlVMEVariant() +{ + cleanup(); +} + +void QQmlVMEVariant::cleanup() +{ + if (type == QVariant::Invalid) { + } else if (type == QMetaType::Int || + type == QMetaType::Bool || + type == QMetaType::Double) { + type = QVariant::Invalid; + } else if (type == QMetaType::QObjectStar) { + ((QQmlGuard<QObject>*)dataPtr())->~QQmlGuard<QObject>(); + type = QVariant::Invalid; + } else if (type == QMetaType::QString) { + ((QString *)dataPtr())->~QString(); + type = QVariant::Invalid; + } else if (type == QMetaType::QUrl) { + ((QUrl *)dataPtr())->~QUrl(); + type = QVariant::Invalid; + } else if (type == QMetaType::QColor) { + ((QColor *)dataPtr())->~QColor(); + type = QVariant::Invalid; + } else if (type == QMetaType::QTime) { + ((QTime *)dataPtr())->~QTime(); + type = QVariant::Invalid; + } else if (type == QMetaType::QDate) { + ((QDate *)dataPtr())->~QDate(); + type = QVariant::Invalid; + } else if (type == QMetaType::QDateTime) { + ((QDateTime *)dataPtr())->~QDateTime(); + type = QVariant::Invalid; + } else if (type == qMetaTypeId<QVariant>()) { + ((QVariant *)dataPtr())->~QVariant(); + type = QVariant::Invalid; + } else if (type == qMetaTypeId<QJSValue>()) { + ((QJSValue *)dataPtr())->~QJSValue(); + type = QVariant::Invalid; + } + +} + +int QQmlVMEVariant::dataType() const +{ + return type; +} + +const void *QQmlVMEVariant::dataPtr() const +{ + return &data; +} + +void *QQmlVMEVariant::dataPtr() +{ + return &data; +} + +QObject *QQmlVMEVariant::asQObject() +{ + if (type != QMetaType::QObjectStar) + setValue((QObject *)0); + + return *(QQmlGuard<QObject> *)(dataPtr()); +} + +const QVariant &QQmlVMEVariant::asQVariant() +{ + if (type != QMetaType::QVariant) + setValue(QVariant()); + + return *(QVariant *)(dataPtr()); +} + +int QQmlVMEVariant::asInt() +{ + if (type != QMetaType::Int) + setValue(int(0)); + + return *(int *)(dataPtr()); +} + +bool QQmlVMEVariant::asBool() +{ + if (type != QMetaType::Bool) + setValue(bool(false)); + + return *(bool *)(dataPtr()); +} + +double QQmlVMEVariant::asDouble() +{ + if (type != QMetaType::Double) + setValue(double(0)); + + return *(double *)(dataPtr()); +} + +const QString &QQmlVMEVariant::asQString() +{ + if (type != QMetaType::QString) + setValue(QString()); + + return *(QString *)(dataPtr()); +} + +const QUrl &QQmlVMEVariant::asQUrl() +{ + if (type != QMetaType::QUrl) + setValue(QUrl()); + + return *(QUrl *)(dataPtr()); +} + +const QColor &QQmlVMEVariant::asQColor() +{ + if (type != QMetaType::QColor) + setValue(QColor()); + + return *(QColor *)(dataPtr()); +} + +const QTime &QQmlVMEVariant::asQTime() +{ + if (type != QMetaType::QTime) + setValue(QTime()); + + return *(QTime *)(dataPtr()); +} + +const QDate &QQmlVMEVariant::asQDate() +{ + if (type != QMetaType::QDate) + setValue(QDate()); + + return *(QDate *)(dataPtr()); +} + +const QDateTime &QQmlVMEVariant::asQDateTime() +{ + if (type != QMetaType::QDateTime) + setValue(QDateTime()); + + return *(QDateTime *)(dataPtr()); +} + +const QJSValue &QQmlVMEVariant::asQJSValue() +{ + if (type != qMetaTypeId<QJSValue>()) + setValue(QJSValue()); + + return *(QJSValue *)(dataPtr()); +} + +void QQmlVMEVariant::setValue(QObject *v) +{ + if (type != QMetaType::QObjectStar) { + cleanup(); + type = QMetaType::QObjectStar; + new (dataPtr()) QQmlGuard<QObject>(); + } + *(QQmlGuard<QObject>*)(dataPtr()) = v; +} + +void QQmlVMEVariant::setValue(const QVariant &v) +{ + if (type != qMetaTypeId<QVariant>()) { + cleanup(); + type = qMetaTypeId<QVariant>(); + new (dataPtr()) QVariant(v); + } else { + *(QVariant *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(int v) +{ + if (type != QMetaType::Int) { + cleanup(); + type = QMetaType::Int; + } + *(int *)(dataPtr()) = v; +} + +void QQmlVMEVariant::setValue(bool v) +{ + if (type != QMetaType::Bool) { + cleanup(); + type = QMetaType::Bool; + } + *(bool *)(dataPtr()) = v; +} + +void QQmlVMEVariant::setValue(double v) +{ + if (type != QMetaType::Double) { + cleanup(); + type = QMetaType::Double; + } + *(double *)(dataPtr()) = v; +} + +void QQmlVMEVariant::setValue(const QString &v) +{ + if (type != QMetaType::QString) { + cleanup(); + type = QMetaType::QString; + new (dataPtr()) QString(v); + } else { + *(QString *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QUrl &v) +{ + if (type != QMetaType::QUrl) { + cleanup(); + type = QMetaType::QUrl; + new (dataPtr()) QUrl(v); + } else { + *(QUrl *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QColor &v) +{ + if (type != QMetaType::QColor) { + cleanup(); + type = QMetaType::QColor; + new (dataPtr()) QColor(v); + } else { + *(QColor *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QTime &v) +{ + if (type != QMetaType::QTime) { + cleanup(); + type = QMetaType::QTime; + new (dataPtr()) QTime(v); + } else { + *(QTime *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QDate &v) +{ + if (type != QMetaType::QDate) { + cleanup(); + type = QMetaType::QDate; + new (dataPtr()) QDate(v); + } else { + *(QDate *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QDateTime &v) +{ + if (type != QMetaType::QDateTime) { + cleanup(); + type = QMetaType::QDateTime; + new (dataPtr()) QDateTime(v); + } else { + *(QDateTime *)(dataPtr()) = v; + } +} + +void QQmlVMEVariant::setValue(const QJSValue &v) +{ + if (type != qMetaTypeId<QJSValue>()) { + cleanup(); + type = qMetaTypeId<QJSValue>(); + new (dataPtr()) QJSValue(v); + } else { + *(QJSValue *)(dataPtr()) = v; + } +} + +QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() +{ + callback = &vmecallback; +} + +void QQmlVMEMetaObjectEndpoint::vmecallback(QQmlNotifierEndpoint *e) +{ + QQmlVMEMetaObjectEndpoint *vmee = static_cast<QQmlVMEMetaObjectEndpoint*>(e); + vmee->tryConnect(); +} + +void QQmlVMEMetaObjectEndpoint::tryConnect() +{ + int aliasId = this - metaObject->aliasEndpoints; + + if (metaObject.flag()) { + // This is actually notify + int sigIdx = metaObject->methodOffset + aliasId + metaObject->metaData->propertyCount; + QMetaObject::activate(metaObject->object, sigIdx, 0); + } else { + QQmlVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId; + if (!d->isObjectAlias()) { + QQmlContextData *ctxt = metaObject->ctxt; + QObject *target = ctxt->idValues[d->contextIdx].data(); + if (!target) + return; + + QMetaProperty prop = target->metaObject()->property(d->propertyIndex()); + if (prop.hasNotifySignal()) + connect(target, prop.notifySignalIndex()); + } + + metaObject.setFlag(); + } +} + +QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, + const QMetaObject *other, + const QQmlVMEMetaData *meta, + QQmlCompiledData *cdata) +: QV8GCCallback::Node(GcPrologueCallback), object(obj), compiledData(cdata), + ctxt(QQmlData::get(obj, true)->outerContext), metaData(meta), data(0), + aliasEndpoints(0), firstVarPropertyIndex(-1), varPropertiesInitialized(false), + v8methods(0), parent(0) +{ + compiledData->addref(); + + *static_cast<QMetaObject *>(this) = *other; + this->d.superdata = obj->metaObject(); + + QObjectPrivate *op = QObjectPrivate::get(obj); + if (op->metaObject) + parent = static_cast<QAbstractDynamicMetaObject*>(op->metaObject); + op->metaObject = this; + + propOffset = QAbstractDynamicMetaObject::propertyOffset(); + methodOffset = QAbstractDynamicMetaObject::methodOffset(); + + data = new QQmlVMEVariant[metaData->propertyCount - metaData->varPropertyCount]; + + aConnected.resize(metaData->aliasCount); + int list_type = qMetaTypeId<QQmlListProperty<QObject> >(); + + // ### Optimize + for (int ii = 0; ii < metaData->propertyCount - metaData->varPropertyCount; ++ii) { + int t = (metaData->propertyData() + ii)->propertyType; + if (t == list_type) { + listProperties.append(List(methodOffset + ii)); + data[ii].setValue(listProperties.count() - 1); + } + } + + firstVarPropertyIndex = metaData->propertyCount - metaData->varPropertyCount; + if (metaData->varPropertyCount) + QV8GCCallback::addGcCallbackNode(this); +} + +QQmlVMEMetaObject::~QQmlVMEMetaObject() +{ + compiledData->release(); + delete parent; + delete [] data; + delete [] aliasEndpoints; + + for (int ii = 0; v8methods && ii < metaData->methodCount; ++ii) { + qPersistentDispose(v8methods[ii]); + } + delete [] v8methods; + + if (metaData->varPropertyCount) + qPersistentDispose(varProperties); // if not weak, will not have been cleaned up by the callback. +} + +int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) +{ + int id = _id; + if(c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast<int*>(a[3]); + if (!(flags & QQmlPropertyPrivate::BypassInterceptor) + && !aInterceptors.isEmpty() + && aInterceptors.testBit(id)) { + QPair<int, QQmlPropertyValueInterceptor*> pair = interceptors.value(id); + int valueIndex = pair.first; + QQmlPropertyValueInterceptor *vi = pair.second; + int type = property(id).userType(); + + if (type != QVariant::Invalid) { + if (valueIndex != -1) { + QQmlEnginePrivate *ep = ctxt?QQmlEnginePrivate::get(ctxt->engine):0; + QQmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[type]; + else valueType = QQmlValueTypeFactory::valueType(type); + Q_ASSERT(valueType); + + valueType->setValue(QVariant(type, a[0])); + QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); + vi->write(valueProp.read(valueType)); + + if (!ep) delete valueType; + return -1; + } else { + vi->write(QVariant(type, a[0])); + return -1; + } + } + } + } + if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { + if (id >= propOffset) { + id -= propOffset; + + if (id < metaData->propertyCount) { + int t = (metaData->propertyData() + id)->propertyType; + bool needActivate = false; + + if (id >= firstVarPropertyIndex) { + Q_ASSERT(t == QMetaType::QVariant); + // the context can be null if accessing var properties from cpp after re-parenting an item. + QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); + QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine(); + if (v8e) { + v8::HandleScope handleScope; + v8::Context::Scope contextScope(v8e->context()); + 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(); + } + + } else { + + if (c == QMetaObject::ReadProperty) { + switch(t) { + case QVariant::Int: + *reinterpret_cast<int *>(a[0]) = data[id].asInt(); + break; + case QVariant::Bool: + *reinterpret_cast<bool *>(a[0]) = data[id].asBool(); + break; + case QVariant::Double: + *reinterpret_cast<double *>(a[0]) = data[id].asDouble(); + break; + case QVariant::String: + *reinterpret_cast<QString *>(a[0]) = data[id].asQString(); + break; + case QVariant::Url: + *reinterpret_cast<QUrl *>(a[0]) = data[id].asQUrl(); + break; + case QVariant::Color: + *reinterpret_cast<QColor *>(a[0]) = data[id].asQColor(); + break; + case QVariant::Date: + *reinterpret_cast<QDate *>(a[0]) = data[id].asQDate(); + break; + case QVariant::DateTime: + *reinterpret_cast<QDateTime *>(a[0]) = data[id].asQDateTime(); + break; + case QMetaType::QObjectStar: + *reinterpret_cast<QObject **>(a[0]) = data[id].asQObject(); + break; + case QMetaType::QVariant: + *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); + break; + default: + break; + } + if (t == qMetaTypeId<QQmlListProperty<QObject> >()) { + int listIndex = data[id].asInt(); + const List *list = &listProperties.at(listIndex); + *reinterpret_cast<QQmlListProperty<QObject> *>(a[0]) = + QQmlListProperty<QObject>(object, (void *)list, + list_append, list_count, list_at, + list_clear); + } + + } else if (c == QMetaObject::WriteProperty) { + + switch(t) { + case QVariant::Int: + needActivate = *reinterpret_cast<int *>(a[0]) != data[id].asInt(); + data[id].setValue(*reinterpret_cast<int *>(a[0])); + break; + case QVariant::Bool: + needActivate = *reinterpret_cast<bool *>(a[0]) != data[id].asBool(); + data[id].setValue(*reinterpret_cast<bool *>(a[0])); + break; + case QVariant::Double: + needActivate = *reinterpret_cast<double *>(a[0]) != data[id].asDouble(); + data[id].setValue(*reinterpret_cast<double *>(a[0])); + break; + case QVariant::String: + needActivate = *reinterpret_cast<QString *>(a[0]) != data[id].asQString(); + data[id].setValue(*reinterpret_cast<QString *>(a[0])); + break; + case QVariant::Url: + needActivate = *reinterpret_cast<QUrl *>(a[0]) != data[id].asQUrl(); + data[id].setValue(*reinterpret_cast<QUrl *>(a[0])); + break; + case QVariant::Color: + needActivate = *reinterpret_cast<QColor *>(a[0]) != data[id].asQColor(); + data[id].setValue(*reinterpret_cast<QColor *>(a[0])); + break; + case QVariant::Date: + needActivate = *reinterpret_cast<QDate *>(a[0]) != data[id].asQDate(); + data[id].setValue(*reinterpret_cast<QDate *>(a[0])); + break; + case QVariant::DateTime: + needActivate = *reinterpret_cast<QDateTime *>(a[0]) != data[id].asQDateTime(); + data[id].setValue(*reinterpret_cast<QDateTime *>(a[0])); + break; + case QMetaType::QObjectStar: + needActivate = *reinterpret_cast<QObject **>(a[0]) != data[id].asQObject(); + data[id].setValue(*reinterpret_cast<QObject **>(a[0])); + break; + case QMetaType::QVariant: + writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); + break; + default: + break; + } + } + + } + + if (c == QMetaObject::WriteProperty && needActivate) { + activate(object, methodOffset + id, 0); + } + + return -1; + } + + id -= metaData->propertyCount; + + if (id < metaData->aliasCount) { + + QQmlVMEMetaData::AliasData *d = metaData->aliasData() + id; + + if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty) + *reinterpret_cast<void **>(a[0]) = 0; + + if (!ctxt) return -1; + + QQmlContext *context = ctxt->asQQmlContext(); + QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); + + QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!target) + return -1; + + connectAlias(id); + + if (d->isObjectAlias()) { + *reinterpret_cast<QObject **>(a[0]) = target; + return -1; + } + + // Remove binding (if any) on write + if(c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast<int*>(a[3]); + if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) { + QQmlData *targetData = QQmlData::get(target); + if (targetData && targetData->hasBindingBit(d->propertyIndex())) { + QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); + if (binding) binding->destroy(); + } + } + } + + if (d->isValueTypeAlias()) { + // Value type property + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); + + QQmlValueType *valueType = ep->valueTypes[d->valueType()]; + Q_ASSERT(valueType); + + valueType->read(target, d->propertyIndex()); + int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a); + + if (c == QMetaObject::WriteProperty) + valueType->write(target, d->propertyIndex(), 0x00); + + return rv; + + } else { + return QMetaObject::metacall(target, c, d->propertyIndex(), a); + } + + } + return -1; + + } + + } else if(c == QMetaObject::InvokeMetaMethod) { + + if (id >= methodOffset) { + + id -= methodOffset; + int plainSignals = metaData->signalCount + metaData->propertyCount + + metaData->aliasCount; + if (id < plainSignals) { + QMetaObject::activate(object, _id, a); + return -1; + } + + id -= plainSignals; + + if (id < metaData->methodCount) { + if (!ctxt->engine) + return -1; // We can't run the method + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. + + v8::Handle<v8::Function> function = method(id); + if (function.IsEmpty()) { + // The function was not compiled. There are some exceptional cases which the + // expression rewriter does not rewrite properly (e.g., \r-terminated lines + // are not rewritten correctly but this bug is deemed out-of-scope to fix for + // performance reasons; see QTBUG-24064) and thus compilation will have failed. + QQmlError e; + e.setDescription(QString(QLatin1String("Exception occurred during compilation of function: %1")). + arg(QLatin1String(QMetaObject::method(_id).signature()))); + ep->warning(e); + return -1; // The dynamic method with that id is not available. + } + + QQmlVMEMetaData::MethodData *data = metaData->methodData() + id; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Handle<v8::Value> *args = 0; + + if (data->parameterCount) { + args = new v8::Handle<v8::Value>[data->parameterCount]; + for (int ii = 0; ii < data->parameterCount; ++ii) + args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]); + } + + v8::TryCatch try_catch; + + v8::Local<v8::Value> result = function->Call(ep->v8engine()->global(), data->parameterCount, args); + + QVariant rv; + if (try_catch.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(try_catch.Message(), error); + if (error.isValid()) + ep->warning(error); + if (a[0]) *(QVariant *)a[0] = QVariant(); + } else { + if (a[0]) *(QVariant *)a[0] = ep->v8engine()->toVariant(result, 0); + } + + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. + return -1; + } + return -1; + } + } + + if (parent) + return parent->metaCall(c, _id, a); + else + return object->qt_metacall(c, _id, a); +} + +v8::Handle<v8::Function> QQmlVMEMetaObject::method(int index) +{ + if (!v8methods) + v8methods = new v8::Persistent<v8::Function>[metaData->methodCount]; + + if (v8methods[index].IsEmpty()) { + QQmlVMEMetaData::MethodData *data = metaData->methodData() + index; + + const char *body = ((const char*)metaData) + data->bodyOffset; + int bodyLength = data->bodyLength; + + // XXX We should evaluate all methods in a single big script block to + // improve the call time between dynamic methods defined on the same + // object + v8methods[index] = QQmlExpressionPrivate::evalFunction(ctxt, object, body, + bodyLength, + ctxt->urlString, + data->lineNumber); + } + + return v8methods[index]; +} + +v8::Handle<v8::Value> QQmlVMEMetaObject::readVarProperty(int id) +{ + Q_ASSERT(id >= firstVarPropertyIndex); + + ensureVarPropertiesAllocated(); + return varProperties->Get(id - firstVarPropertyIndex); +} + +QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) +{ + if (id >= firstVarPropertyIndex) { + ensureVarPropertiesAllocated(); + return QQmlEnginePrivate::get(ctxt->engine)->v8engine()->toVariant(varProperties->Get(id - firstVarPropertyIndex), -1); + } else { + if (data[id].dataType() == QMetaType::QObjectStar) { + return QVariant::fromValue(data[id].asQObject()); + } else { + return data[id].asQVariant(); + } + } +} + +void QQmlVMEMetaObject::writeVarProperty(int id, v8::Handle<v8::Value> value) +{ + Q_ASSERT(id >= firstVarPropertyIndex); + ensureVarPropertiesAllocated(); + + // Importantly, if the current value is a scarce resource, we need to ensure that it + // gets automatically released by the engine if no other references to it exist. + v8::Local<v8::Value> oldv = varProperties->Get(id - firstVarPropertyIndex); + if (oldv->IsObject()) { + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(oldv)); + if (r) { + r->removeVmePropertyReference(); + } + } + + // And, if the new value is a scarce resource, we need to ensure that it does not get + // automatically released by the engine until no other references to it exist. + if (value->IsObject()) { + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(value)); + if (r) { + r->addVmePropertyReference(); + } + } + + // Write the value and emit change signal as appropriate. + varProperties->Set(id - firstVarPropertyIndex, value); + activate(object, methodOffset + id, 0); +} + +void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) +{ + if (id >= firstVarPropertyIndex) { + ensureVarPropertiesAllocated(); + + // Importantly, if the current value is a scarce resource, we need to ensure that it + // gets automatically released by the engine if no other references to it exist. + v8::Local<v8::Value> oldv = varProperties->Get(id - firstVarPropertyIndex); + if (oldv->IsObject()) { + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(oldv)); + if (r) { + r->removeVmePropertyReference(); + } + } + + // And, if the new value is a scarce resource, we need to ensure that it does not get + // automatically released by the engine until no other references to it exist. + v8::Handle<v8::Value> newv = QQmlEnginePrivate::get(ctxt->engine)->v8engine()->fromVariant(value); + if (newv->IsObject()) { + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(newv)); + if (r) { + r->addVmePropertyReference(); + } + } + + // Write the value and emit change signal as appropriate. + QVariant currentValue = readPropertyAsVariant(id); + varProperties->Set(id - firstVarPropertyIndex, newv); + if ((currentValue.userType() != value.userType() || currentValue != value)) + activate(object, methodOffset + id, 0); + } else { + bool needActivate = false; + if (value.userType() == QMetaType::QObjectStar) { + QObject *o = qvariant_cast<QObject *>(value); + needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o); + data[id].setValue(qvariant_cast<QObject *>(value)); + } else { + needActivate = (data[id].dataType() != qMetaTypeId<QVariant>() || + data[id].asQVariant().userType() != value.userType() || + data[id].asQVariant() != value); + data[id].setValue(value); + } + + if (needActivate) + activate(object, methodOffset + id, 0); + } +} + +void QQmlVMEMetaObject::listChanged(int id) +{ + activate(object, methodOffset + id, 0); +} + +void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o) +{ + List *list = static_cast<List *>(prop->data); + list->append(o); + QMetaObject::activate(prop->object, list->notifyIndex, 0); +} + +int QQmlVMEMetaObject::list_count(QQmlListProperty<QObject> *prop) +{ + return static_cast<List *>(prop->data)->count(); +} + +QObject *QQmlVMEMetaObject::list_at(QQmlListProperty<QObject> *prop, int index) +{ + return static_cast<List *>(prop->data)->at(index); +} + +void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop) +{ + List *list = static_cast<List *>(prop->data); + list->clear(); + QMetaObject::activate(prop->object, list->notifyIndex, 0); +} + +void QQmlVMEMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor) +{ + if (aInterceptors.isEmpty()) + aInterceptors.resize(propertyCount() + metaData->propertyCount); + aInterceptors.setBit(index); + interceptors.insert(index, qMakePair(valueIndex, interceptor)); +} + +int QQmlVMEMetaObject::vmeMethodLineNumber(int index) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast<QQmlVMEMetaObject *>(parent)->vmeMethodLineNumber(index); + } + + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + + int rawIndex = index - methodOffset - plainSignals; + + QQmlVMEMetaData::MethodData *data = metaData->methodData() + rawIndex; + return data->lineNumber; +} + +v8::Handle<v8::Function> QQmlVMEMetaObject::vmeMethod(int index) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast<QQmlVMEMetaObject *>(parent)->vmeMethod(index); + } + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + return method(index - methodOffset - plainSignals); +} + +// Used by debugger +void QQmlVMEMetaObject::setVmeMethod(int index, v8::Persistent<v8::Function> value) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast<QQmlVMEMetaObject *>(parent)->setVmeMethod(index, value); + } + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + + if (!v8methods) + v8methods = new v8::Persistent<v8::Function>[metaData->methodCount]; + + int methodIndex = index - methodOffset - plainSignals; + if (!v8methods[methodIndex].IsEmpty()) + qPersistentDispose(v8methods[methodIndex]); + v8methods[methodIndex] = value; +} + +v8::Handle<v8::Value> QQmlVMEMetaObject::vmeProperty(int index) +{ + if (index < propOffset) { + Q_ASSERT(parent); + return static_cast<QQmlVMEMetaObject *>(parent)->vmeProperty(index); + } + return readVarProperty(index - propOffset); +} + +void QQmlVMEMetaObject::setVMEProperty(int index, v8::Handle<v8::Value> v) +{ + if (index < propOffset) { + Q_ASSERT(parent); + static_cast<QQmlVMEMetaObject *>(parent)->setVMEProperty(index, v); + return; + } + return writeVarProperty(index - propOffset, v); +} + +void QQmlVMEMetaObject::ensureVarPropertiesAllocated() +{ + if (!varPropertiesInitialized) + allocateVarPropertiesArray(); +} + +// see also: QV8GCCallback::garbageCollectorPrologueCallback() +void QQmlVMEMetaObject::allocateVarPropertiesArray() +{ + v8::HandleScope handleScope; + v8::Context::Scope cs(QQmlEnginePrivate::get(ctxt->engine)->v8engine()->context()); + varProperties = qPersistentNew(v8::Array::New(metaData->varPropertyCount)); + varProperties.MakeWeak(static_cast<void*>(this), VarPropertiesWeakReferenceCallback); + varPropertiesInitialized = true; +} + +/* + The "var" properties are stored in a v8::Array which will be strong persistent if the object has cpp-ownership + and the root QObject in the parent chain does not have JS-ownership. In the weak persistent handle case, + this callback will dispose the handle when the v8object which owns the lifetime of the var properties array + is cleared as a result of all other handles to that v8object being released. + See QV8GCCallback::garbageCollectorPrologueCallback() for more information. + */ +void QQmlVMEMetaObject::VarPropertiesWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter) +{ + QQmlVMEMetaObject *vmemo = static_cast<QQmlVMEMetaObject*>(parameter); + Q_ASSERT(vmemo); + qPersistentDispose(object); + vmemo->varProperties.Clear(); +} + +void QQmlVMEMetaObject::GcPrologueCallback(QV8GCCallback::Node *node) +{ + QQmlVMEMetaObject *vmemo = static_cast<QQmlVMEMetaObject*>(node); + Q_ASSERT(vmemo); + if (!vmemo->varPropertiesInitialized || vmemo->varProperties.IsEmpty() || !vmemo->ctxt || !vmemo->ctxt->engine) + return; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(vmemo->ctxt->engine); + ep->v8engine()->addRelationshipForGC(vmemo->object, vmemo->varProperties); +} + +bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const +{ + Q_ASSERT(index >= propOffset + metaData->propertyCount); + + *target = 0; + *coreIndex = -1; + *valueTypeIndex = -1; + + if (!ctxt) + return false; + + QQmlVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset - metaData->propertyCount); + QQmlContext *context = ctxt->asQQmlContext(); + QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); + + *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!*target) + return false; + + if (d->isObjectAlias()) { + } else if (d->isValueTypeAlias()) { + *coreIndex = d->propertyIndex(); + *valueTypeIndex = d->valueTypeIndex(); + } else { + *coreIndex = d->propertyIndex(); + } + + return true; +} + +void QQmlVMEMetaObject::connectAlias(int aliasId) +{ + if (!aConnected.testBit(aliasId)) { + + if (!aliasEndpoints) + aliasEndpoints = new QQmlVMEMetaObjectEndpoint[metaData->aliasCount]; + + aConnected.setBit(aliasId); + + QQmlVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; + + QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId; + endpoint->metaObject = this; + + endpoint->connect(&ctxt->idValues[d->contextIdx].bindings); + + endpoint->tryConnect(); + } +} + +void QQmlVMEMetaObject::connectAliasSignal(int index) +{ + int aliasId = (index - methodOffset) - metaData->propertyCount; + if (aliasId < 0 || aliasId >= metaData->aliasCount) + return; + + connectAlias(aliasId); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h new file mode 100644 index 0000000000..deee989383 --- /dev/null +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLVMEMETAOBJECT_P_H +#define QQMLVMEMETAOBJECT_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 "qqml.h" + +#include <QtCore/QMetaObject> +#include <QtCore/QBitArray> +#include <QtCore/QPair> +#include <QtGui/QColor> +#include <QtCore/QDate> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +#include "qqmlguard_p.h" +#include "qqmlcompiler_p.h" +#include "qqmlcontext_p.h" + +#include <private/qv8engine_p.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +#define QML_ALIAS_FLAG_PTR 0x00000001 + +struct QQmlVMEMetaData +{ + short varPropertyCount; + short propertyCount; + short aliasCount; + short signalCount; + short methodCount; + short dummyForAlignment; // Add padding to ensure that the following + // AliasData/PropertyData/MethodData is int aligned. + + struct AliasData { + int contextIdx; + int propertyIdx; + int flags; + + bool isObjectAlias() const { + return propertyIdx == -1; + } + bool isPropertyAlias() const { + return !isObjectAlias() && !(propertyIdx & 0xFF000000); + } + bool isValueTypeAlias() const { + return !isObjectAlias() && (propertyIdx & 0xFF000000); + } + int propertyIndex() const { + return propertyIdx & 0x0000FFFF; + } + int valueTypeIndex() const { + return (propertyIdx & 0x00FF0000) >> 16; + } + int valueType() const { + return ((unsigned int)propertyIdx) >> 24; + } + }; + + struct PropertyData { + int propertyType; + }; + + struct MethodData { + int parameterCount; + int bodyOffset; + int bodyLength; + int lineNumber; + }; + + PropertyData *propertyData() const { + return (PropertyData *)(((const char *)this) + sizeof(QQmlVMEMetaData)); + } + + AliasData *aliasData() const { + return (AliasData *)(propertyData() + propertyCount); + } + + MethodData *methodData() const { + return (MethodData *)(aliasData() + aliasCount); + } +}; + +class QV8QObjectWrapper; +class QQmlVMEVariant; +class QQmlRefCount; +class QQmlVMEMetaObjectEndpoint; +class Q_AUTOTEST_EXPORT QQmlVMEMetaObject : public QAbstractDynamicMetaObject, + public QV8GCCallback::Node +{ +public: + QQmlVMEMetaObject(QObject *obj, const QMetaObject *other, const QQmlVMEMetaData *data, + QQmlCompiledData *compiledData); + ~QQmlVMEMetaObject(); + + bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; + void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor); + v8::Handle<v8::Function> vmeMethod(int index); + int vmeMethodLineNumber(int index); + void setVmeMethod(int index, v8::Persistent<v8::Function>); + v8::Handle<v8::Value> vmeProperty(int index); + void setVMEProperty(int index, v8::Handle<v8::Value> v); + + void connectAliasSignal(int index); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + friend class QQmlVMEMetaObjectEndpoint; + + QObject *object; + QQmlCompiledData *compiledData; + QQmlGuardedContextData ctxt; + + const QQmlVMEMetaData *metaData; + int propOffset; + int methodOffset; + + QQmlVMEVariant *data; + QQmlVMEMetaObjectEndpoint *aliasEndpoints; + + v8::Persistent<v8::Array> varProperties; + int firstVarPropertyIndex; + bool varPropertiesInitialized; + static void VarPropertiesWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter); + static void GcPrologueCallback(QV8GCCallback::Node *node); + inline void allocateVarPropertiesArray(); + inline void ensureVarPropertiesAllocated(); + + void connectAlias(int aliasId); + QBitArray aConnected; + QBitArray aInterceptors; + QHash<int, QPair<int, QQmlPropertyValueInterceptor*> > interceptors; + + v8::Persistent<v8::Function> *v8methods; + v8::Handle<v8::Function> method(int); + + v8::Handle<v8::Value> readVarProperty(int); + void writeVarProperty(int, v8::Handle<v8::Value>); + QVariant readPropertyAsVariant(int); + void writeProperty(int, const QVariant &); + + QAbstractDynamicMetaObject *parent; + + void listChanged(int); + class List : public QList<QObject*> + { + public: + List(int lpi) : notifyIndex(lpi) {} + int notifyIndex; + }; + QList<List> listProperties; + + static void list_append(QQmlListProperty<QObject> *, QObject *); + static int list_count(QQmlListProperty<QObject> *); + static QObject *list_at(QQmlListProperty<QObject> *, int); + static void list_clear(QQmlListProperty<QObject> *); + + friend class QV8GCCallback; + friend class QV8QObjectWrapper; +}; + +QT_END_NAMESPACE + +#endif // QQMLVMEMETAOBJECT_P_H diff --git a/src/qml/qml/qqmlwatcher.cpp b/src/qml/qml/qqmlwatcher.cpp new file mode 100644 index 0000000000..500185762f --- /dev/null +++ b/src/qml/qml/qqmlwatcher.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlwatcher_p.h" + +#include "qqmlexpression.h" +#include "qqmlcontext.h" +#include "qqml.h" + +#include <private/qqmldebugservice_p.h> +#include "qqmlproperty_p.h" +#include "qqmlvaluetype_p.h" + +#include <QtCore/qmetaobject.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + + +class QQmlWatchProxy : public QObject +{ + Q_OBJECT +public: + QQmlWatchProxy(int id, + QObject *object, + int debugId, + const QMetaProperty &prop, + QQmlWatcher *parent = 0); + + QQmlWatchProxy(int id, + QQmlExpression *exp, + int debugId, + QQmlWatcher *parent = 0); + +public slots: + void notifyValueChanged(); + +private: + friend class QQmlWatcher; + int m_id; + QQmlWatcher *m_watch; + QObject *m_object; + int m_debugId; + QMetaProperty m_property; + + QQmlExpression *m_expr; +}; + +QQmlWatchProxy::QQmlWatchProxy(int id, + QQmlExpression *exp, + int debugId, + QQmlWatcher *parent) +: QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) +{ + QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged())); +} + +QQmlWatchProxy::QQmlWatchProxy(int id, + QObject *object, + int debugId, + const QMetaProperty &prop, + QQmlWatcher *parent) +: QObject(parent), m_id(id), m_watch(parent), m_object(object), m_debugId(debugId), m_property(prop), m_expr(0) +{ + static int refreshIdx = -1; + if(refreshIdx == -1) + refreshIdx = QQmlWatchProxy::staticMetaObject.indexOfMethod("notifyValueChanged()"); + + if (prop.hasNotifySignal()) + QQmlPropertyPrivate::connect(m_object, prop.notifySignalIndex(), this, refreshIdx); +} + +void QQmlWatchProxy::notifyValueChanged() +{ + QVariant v; + if (m_expr) + v = m_expr->evaluate(); + else if (QQmlValueTypeFactory::isValueType(m_property.userType())) + v = m_property.read(m_object); + + emit m_watch->propertyChanged(m_id, m_debugId, m_property, v); +} + + +QQmlWatcher::QQmlWatcher(QObject *parent) + : QObject(parent) +{ +} + +bool QQmlWatcher::addWatch(int id, quint32 debugId) +{ + QObject *object = QQmlDebugService::objectForId(debugId); + if (object) { + int propCount = object->metaObject()->propertyCount(); + for (int ii=0; ii<propCount; ii++) + addPropertyWatch(id, object, debugId, object->metaObject()->property(ii)); + return true; + } + return false; +} + +bool QQmlWatcher::addWatch(int id, quint32 debugId, const QByteArray &property) +{ + QObject *object = QQmlDebugService::objectForId(debugId); + if (object) { + int index = object->metaObject()->indexOfProperty(property.constData()); + if (index >= 0) { + addPropertyWatch(id, object, debugId, object->metaObject()->property(index)); + return true; + } + } + return false; +} + +bool QQmlWatcher::addWatch(int id, quint32 objectId, const QString &expr) +{ + QObject *object = QQmlDebugService::objectForId(objectId); + QQmlContext *context = qmlContext(object); + if (context) { + QQmlExpression *exprObj = new QQmlExpression(context, object, expr); + exprObj->setNotifyOnValueChanged(true); + QQmlWatchProxy *proxy = new QQmlWatchProxy(id, exprObj, objectId, this); + exprObj->setParent(proxy); + m_proxies[id].append(proxy); + proxy->notifyValueChanged(); + return true; + } + return false; +} + +void QQmlWatcher::removeWatch(int id) +{ + if (!m_proxies.contains(id)) + return; + + QList<QPointer<QQmlWatchProxy> > proxies = m_proxies.take(id); + qDeleteAll(proxies); +} + +void QQmlWatcher::addPropertyWatch(int id, QObject *object, quint32 debugId, const QMetaProperty &property) +{ + QQmlWatchProxy *proxy = new QQmlWatchProxy(id, object, debugId, property, this); + m_proxies[id].append(proxy); + + proxy->notifyValueChanged(); +} + +QT_END_NAMESPACE + +#include <qqmlwatcher.moc> diff --git a/src/qml/qml/qqmlwatcher_p.h b/src/qml/qml/qqmlwatcher_p.h new file mode 100644 index 0000000000..70dc9d468c --- /dev/null +++ b/src/qml/qml/qqmlwatcher_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLWATCHER_P_H +#define QQMLWATCHER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qhash.h> +#include <QtCore/qset.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +class QQmlWatchProxy; +class QQmlExpression; +class QQmlContext; +class QMetaProperty; + +class QQmlWatcher : public QObject +{ + Q_OBJECT +public: + QQmlWatcher(QObject * = 0); + + bool addWatch(int id, quint32 objectId); + bool addWatch(int id, quint32 objectId, const QByteArray &property); + bool addWatch(int id, quint32 objectId, const QString &expr); + + void removeWatch(int id); + +Q_SIGNALS: + void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); + +private: + friend class QQmlWatchProxy; + void addPropertyWatch(int id, QObject *object, quint32 objectId, const QMetaProperty &property); + + QHash<int, QList<QPointer<QQmlWatchProxy> > > m_proxies; +}; + +QT_END_NAMESPACE + +#endif // QQMLWATCHER_P_H diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp new file mode 100644 index 0000000000..122693ad91 --- /dev/null +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -0,0 +1,1797 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlxmlhttprequest_p.h" + +#include <private/qv8engine_p.h> + +#include "qqmlengine.h" +#include "qqmlengine_p.h" +#include <private/qqmlrefcount_p.h> +#include "qqmlengine_p.h" +#include "qqmlexpression_p.h" +#include "qqmlglobal_p.h" +#include <private/qv8domerrors_p.h> + +#include <QtCore/qobject.h> +#include <QtQml/qjsvalue.h> +#include <QtQml/qjsengine.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qtextcodec.h> +#include <QtCore/qxmlstream.h> +#include <QtCore/qstack.h> +#include <QtCore/qdebug.h> + +#include <QtCore/QStringBuilder> + +#ifndef QT_NO_XMLSTREAMREADER + +#define V8THROW_REFERENCE(string) { \ + v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} + +#define D(arg) (arg)->release() +#define A(arg) (arg)->addref() + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP); + +struct QQmlXMLHttpRequestData { + QQmlXMLHttpRequestData(); + ~QQmlXMLHttpRequestData(); + + v8::Persistent<v8::Function> nodeFunction; + + v8::Persistent<v8::Object> namedNodeMapPrototype; + v8::Persistent<v8::Object> nodeListPrototype; + v8::Persistent<v8::Object> nodePrototype; + v8::Persistent<v8::Object> elementPrototype; + v8::Persistent<v8::Object> attrPrototype; + v8::Persistent<v8::Object> characterDataPrototype; + v8::Persistent<v8::Object> textPrototype; + v8::Persistent<v8::Object> cdataPrototype; + v8::Persistent<v8::Object> documentPrototype; + + v8::Local<v8::Object> newNode(); +}; + +static inline QQmlXMLHttpRequestData *xhrdata(QV8Engine *engine) +{ + return (QQmlXMLHttpRequestData *)engine->xmlHttpRequestData(); +} + +QQmlXMLHttpRequestData::QQmlXMLHttpRequestData() +{ +} + +QQmlXMLHttpRequestData::~QQmlXMLHttpRequestData() +{ + qPersistentDispose(nodeFunction); + qPersistentDispose(namedNodeMapPrototype); + qPersistentDispose(nodeListPrototype); + qPersistentDispose(nodePrototype); + qPersistentDispose(elementPrototype); + qPersistentDispose(attrPrototype); + qPersistentDispose(characterDataPrototype); + qPersistentDispose(textPrototype); + qPersistentDispose(cdataPrototype); + qPersistentDispose(documentPrototype); +} + +v8::Local<v8::Object> QQmlXMLHttpRequestData::newNode() +{ + if (nodeFunction.IsEmpty()) { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + nodeFunction = qPersistentNew<v8::Function>(ft->GetFunction()); + } + + return nodeFunction->NewInstance(); +} + +namespace { + +class DocumentImpl; +class NodeImpl +{ +public: + NodeImpl() : type(Element), document(0), parent(0) {} + virtual ~NodeImpl() { + for (int ii = 0; ii < children.count(); ++ii) + delete children.at(ii); + for (int ii = 0; ii < attributes.count(); ++ii) + delete attributes.at(ii); + } + + // These numbers are copied from the Node IDL definition + enum Type { + Attr = 2, + CDATA = 4, + Comment = 8, + Document = 9, + DocumentFragment = 11, + DocumentType = 10, + Element = 1, + Entity = 6, + EntityReference = 5, + Notation = 12, + ProcessingInstruction = 7, + Text = 3 + }; + Type type; + + QString namespaceUri; + QString name; + + QString data; + + void addref(); + void release(); + + DocumentImpl *document; + NodeImpl *parent; + + QList<NodeImpl *> children; + QList<NodeImpl *> attributes; +}; + +class DocumentImpl : public QQmlRefCount, public NodeImpl +{ +public: + DocumentImpl() : root(0) { type = Document; } + virtual ~DocumentImpl() { + if (root) delete root; + } + + QString version; + QString encoding; + bool isStandalone; + + NodeImpl *root; + + void addref() { QQmlRefCount::addref(); } + void release() { QQmlRefCount::release(); } +}; + +class NamedNodeMap +{ +public: + // JS API + static v8::Handle<v8::Value> length(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> indexed(uint32_t index, const v8::AccessorInfo& info); + static v8::Handle<v8::Value> named(v8::Local<v8::String> property, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); + static v8::Handle<v8::Value> create(QV8Engine *, NodeImpl *, QList<NodeImpl *> *); +}; + +class NodeList +{ +public: + // JS API + static v8::Handle<v8::Value> length(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> indexed(uint32_t index, const v8::AccessorInfo& info); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); + static v8::Handle<v8::Value> create(QV8Engine *, NodeImpl *); +}; + +class Node +{ +public: + // JS API + static v8::Handle<v8::Value> nodeName(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> nodeValue(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> nodeType(v8::Local<v8::String>, const v8::AccessorInfo& args); + + static v8::Handle<v8::Value> parentNode(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> childNodes(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> firstChild(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> lastChild(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> previousSibling(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> nextSibling(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> attributes(v8::Local<v8::String>, const v8::AccessorInfo& args); + + //static v8::Handle<v8::Value> ownerDocument(v8::Local<v8::String>, const v8::AccessorInfo& args); + //static v8::Handle<v8::Value> namespaceURI(v8::Local<v8::String>, const v8::AccessorInfo& args); + //static v8::Handle<v8::Value> prefix(v8::Local<v8::String>, const v8::AccessorInfo& args); + //static v8::Handle<v8::Value> localName(v8::Local<v8::String>, const v8::AccessorInfo& args); + //static v8::Handle<v8::Value> baseURI(v8::Local<v8::String>, const v8::AccessorInfo& args); + //static v8::Handle<v8::Value> textContent(v8::Local<v8::String>, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); + static v8::Handle<v8::Value> create(QV8Engine *, NodeImpl *); + + Node(); + Node(const Node &o); + ~Node(); + bool isNull() const; + + NodeImpl *d; + +private: + Node &operator=(const Node &); +}; + +class Element : public Node +{ +public: + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); +}; + +class Attr : public Node +{ +public: + // JS API + static v8::Handle<v8::Value> name(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> specified(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> value(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> ownerElement(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> schemaTypeInfo(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> isId(v8::Local<v8::String>, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); +}; + +class CharacterData : public Node +{ +public: + // JS API + static v8::Handle<v8::Value> length(v8::Local<v8::String>, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); +}; + +class Text : public CharacterData +{ +public: + // JS API + static v8::Handle<v8::Value> isElementContentWhitespace(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> wholeText(v8::Local<v8::String>, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); +}; + +class CDATA : public Text +{ +public: + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); +}; + +class Document : public Node +{ +public: + // JS API + static v8::Handle<v8::Value> xmlVersion(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> xmlEncoding(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> xmlStandalone(v8::Local<v8::String>, const v8::AccessorInfo& args); + static v8::Handle<v8::Value> documentElement(v8::Local<v8::String>, const v8::AccessorInfo& args); + + // C++ API + static v8::Handle<v8::Object> prototype(QV8Engine *); + static v8::Handle<v8::Value> load(QV8Engine *engine, const QByteArray &data); +}; + +} + +class QQmlDOMNodeResource : public QV8ObjectResource, public Node +{ + V8_RESOURCE_TYPE(DOMNodeType); +public: + QQmlDOMNodeResource(QV8Engine *e); + + QList<NodeImpl *> *list; // Only used in NamedNodeMap +}; + +QQmlDOMNodeResource::QQmlDOMNodeResource(QV8Engine *e) +: QV8ObjectResource(e), list(0) +{ +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Node) +Q_DECLARE_METATYPE(NodeList) +Q_DECLARE_METATYPE(NamedNodeMap) + +QT_BEGIN_NAMESPACE + +void NodeImpl::addref() +{ + A(document); +} + +void NodeImpl::release() +{ + D(document); +} + +v8::Handle<v8::Value> Node::nodeName(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + switch (r->d->type) { + case NodeImpl::Document: + return v8::String::New("#document"); + case NodeImpl::CDATA: + return v8::String::New("#cdata-section"); + case NodeImpl::Text: + return v8::String::New("#text"); + default: + return engine->toString(r->d->name); + } +} + +v8::Handle<v8::Value> Node::nodeValue(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (r->d->type == NodeImpl::Document || + r->d->type == NodeImpl::DocumentFragment || + r->d->type == NodeImpl::DocumentType || + r->d->type == NodeImpl::Element || + r->d->type == NodeImpl::Entity || + r->d->type == NodeImpl::EntityReference || + r->d->type == NodeImpl::Notation) + return v8::Null(); + + return engine->toString(r->d->data); +} + +v8::Handle<v8::Value> Node::nodeType(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + return v8::Integer::New(r->d->type); +} + +v8::Handle<v8::Value> Node::parentNode(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (r->d->parent) return Node::create(engine, r->d->parent); + else return v8::Null(); +} + +v8::Handle<v8::Value> Node::childNodes(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return NodeList::create(engine, r->d); +} + +v8::Handle<v8::Value> Node::firstChild(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (r->d->children.isEmpty()) return v8::Null(); + else return Node::create(engine, r->d->children.first()); +} + +v8::Handle<v8::Value> Node::lastChild(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (r->d->children.isEmpty()) return v8::Null(); + else return Node::create(engine, r->d->children.last()); +} + +v8::Handle<v8::Value> Node::previousSibling(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (!r->d->parent) return v8::Null(); + + for (int ii = 0; ii < r->d->parent->children.count(); ++ii) { + if (r->d->parent->children.at(ii) == r->d) { + if (ii == 0) return v8::Null(); + else return Node::create(engine, r->d->parent->children.at(ii - 1)); + } + } + + return v8::Null(); +} + +v8::Handle<v8::Value> Node::nextSibling(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (!r->d->parent) return v8::Null(); + + for (int ii = 0; ii < r->d->parent->children.count(); ++ii) { + if (r->d->parent->children.at(ii) == r->d) { + if ((ii + 1) == r->d->parent->children.count()) return v8::Null(); + else return Node::create(engine, r->d->parent->children.at(ii + 1)); + } + } + + return v8::Null(); +} + +v8::Handle<v8::Value> Node::attributes(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if (r->d->type != NodeImpl::Element) + return v8::Null(); + else + return NamedNodeMap::create(engine, r->d, &r->d->attributes); +} + +v8::Handle<v8::Object> Node::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->nodePrototype.IsEmpty()) { + d->nodePrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->nodePrototype->SetAccessor(v8::String::New("nodeName"), nodeName, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("nodeValue"), nodeValue, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("nodeType"), nodeType, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("parentNode"), parentNode, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("childNodes"), childNodes, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("firstChild"), firstChild, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("lastChild"), lastChild, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("previousSibling"), previousSibling, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("nextSibling"), nextSibling, + 0, v8::External::Wrap(engine)); + d->nodePrototype->SetAccessor(v8::String::New("attributes"), attributes, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->nodePrototype); + } + return d->nodePrototype; +} + +v8::Handle<v8::Value> Node::create(QV8Engine *engine, NodeImpl *data) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + v8::Local<v8::Object> instance = d->newNode(); + + switch (data->type) { + case NodeImpl::Attr: + instance->SetPrototype(Attr::prototype(engine)); + break; + case NodeImpl::Comment: + case NodeImpl::Document: + case NodeImpl::DocumentFragment: + case NodeImpl::DocumentType: + case NodeImpl::Entity: + case NodeImpl::EntityReference: + case NodeImpl::Notation: + case NodeImpl::ProcessingInstruction: + return v8::Undefined(); + case NodeImpl::CDATA: + instance->SetPrototype(CDATA::prototype(engine)); + break; + case NodeImpl::Text: + instance->SetPrototype(Text::prototype(engine)); + break; + case NodeImpl::Element: + instance->SetPrototype(Element::prototype(engine)); + break; + } + + QQmlDOMNodeResource *r = new QQmlDOMNodeResource(engine); + r->d = data; + if (data) A(data); + instance->SetExternalResource(r); + + return instance; +} + +v8::Handle<v8::Object> Element::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->elementPrototype.IsEmpty()) { + d->elementPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->elementPrototype->SetPrototype(Node::prototype(engine)); + d->elementPrototype->SetAccessor(v8::String::New("tagName"), nodeName, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->elementPrototype); + } + return d->elementPrototype; +} + +v8::Handle<v8::Object> Attr::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->attrPrototype.IsEmpty()) { + d->attrPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->attrPrototype->SetPrototype(Node::prototype(engine)); + d->attrPrototype->SetAccessor(v8::String::New("name"), name, + 0, v8::External::Wrap(engine)); + d->attrPrototype->SetAccessor(v8::String::New("value"), value, + 0, v8::External::Wrap(engine)); + d->attrPrototype->SetAccessor(v8::String::New("ownerElement"), ownerElement, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->attrPrototype); + } + return d->attrPrototype; +} + +v8::Handle<v8::Value> Attr::name(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return engine->toString(r->d->name); +} + +v8::Handle<v8::Value> Attr::value(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return engine->toString(r->d->data); +} + +v8::Handle<v8::Value> Attr::ownerElement(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return Node::create(engine, r->d->parent); +} + +v8::Handle<v8::Value> CharacterData::length(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + Q_UNUSED(engine) + return v8::Integer::New(r->d->data.length()); +} + +v8::Handle<v8::Object> CharacterData::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->characterDataPrototype.IsEmpty()) { + d->characterDataPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->characterDataPrototype->SetPrototype(Node::prototype(engine)); + d->characterDataPrototype->SetAccessor(v8::String::New("data"), nodeValue, + 0, v8::External::Wrap(engine)); + d->characterDataPrototype->SetAccessor(v8::String::New("length"), length, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->characterDataPrototype); + } + return d->characterDataPrototype; +} + +v8::Handle<v8::Value> Text::isElementContentWhitespace(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + Q_UNUSED(engine) + return v8::Boolean::New(r->d->data.trimmed().isEmpty()); +} + +v8::Handle<v8::Value> Text::wholeText(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return engine->toString(r->d->data); +} + +v8::Handle<v8::Object> Text::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->textPrototype.IsEmpty()) { + d->textPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->textPrototype->SetPrototype(CharacterData::prototype(engine)); + d->textPrototype->SetAccessor(v8::String::New("isElementContentWhitespace"), isElementContentWhitespace, + 0, v8::External::Wrap(engine)); + d->textPrototype->SetAccessor(v8::String::New("wholeText"), wholeText, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->textPrototype); + } + return d->textPrototype; +} + +v8::Handle<v8::Object> CDATA::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->cdataPrototype.IsEmpty()) { + d->cdataPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->cdataPrototype->SetPrototype(Text::prototype(engine)); + engine->freezeObject(d->cdataPrototype); + } + return d->cdataPrototype; +} + +v8::Handle<v8::Object> Document::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->documentPrototype.IsEmpty()) { + d->documentPrototype = qPersistentNew<v8::Object>(v8::Object::New()); + d->documentPrototype->SetPrototype(Node::prototype(engine)); + d->documentPrototype->SetAccessor(v8::String::New("xmlVersion"), xmlVersion, + 0, v8::External::Wrap(engine)); + d->documentPrototype->SetAccessor(v8::String::New("xmlEncoding"), xmlEncoding, + 0, v8::External::Wrap(engine)); + d->documentPrototype->SetAccessor(v8::String::New("xmlStandalone"), xmlStandalone, + 0, v8::External::Wrap(engine)); + d->documentPrototype->SetAccessor(v8::String::New("documentElement"), documentElement, + 0, v8::External::Wrap(engine)); + engine->freezeObject(d->documentPrototype); + } + return d->documentPrototype; +} + +v8::Handle<v8::Value> Document::load(QV8Engine *engine, const QByteArray &data) +{ + Q_ASSERT(engine); + + DocumentImpl *document = 0; + QStack<NodeImpl *> nodeStack; + + QXmlStreamReader reader(data); + + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + Q_ASSERT(!document); + document = new DocumentImpl; + document->document = document; + document->version = reader.documentVersion().toString(); + document->encoding = reader.documentEncoding().toString(); + document->isStandalone = reader.isStandaloneDocument(); + break; + case QXmlStreamReader::EndDocument: + break; + case QXmlStreamReader::StartElement: + { + Q_ASSERT(document); + NodeImpl *node = new NodeImpl; + node->document = document; + node->namespaceUri = reader.namespaceUri().toString(); + node->name = reader.name().toString(); + if (nodeStack.isEmpty()) { + document->root = node; + } else { + node->parent = nodeStack.top(); + node->parent->children.append(node); + } + nodeStack.append(node); + + foreach (const QXmlStreamAttribute &a, reader.attributes()) { + NodeImpl *attr = new NodeImpl; + attr->document = document; + attr->type = NodeImpl::Attr; + attr->namespaceUri = a.namespaceUri().toString(); + attr->name = a.name().toString(); + attr->data = a.value().toString(); + attr->parent = node; + node->attributes.append(attr); + } + } + break; + case QXmlStreamReader::EndElement: + nodeStack.pop(); + break; + case QXmlStreamReader::Characters: + { + NodeImpl *node = new NodeImpl; + node->document = document; + node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text; + node->parent = nodeStack.top(); + node->parent->children.append(node); + node->data = reader.text().toString(); + } + break; + case QXmlStreamReader::Comment: + break; + case QXmlStreamReader::DTD: + break; + case QXmlStreamReader::EntityReference: + break; + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + if (!document || reader.hasError()) { + if (document) D(document); + return v8::Null(); + } + + v8::Local<v8::Object> instance = xhrdata(engine)->newNode(); + QQmlDOMNodeResource *r = new QQmlDOMNodeResource(engine); + r->d = document; + instance->SetExternalResource(r); + instance->SetPrototype(Document::prototype(engine)); + return instance; +} + +Node::Node() +: d(0) +{ +} + +Node::Node(const Node &o) +: d(o.d) +{ + if (d) A(d); +} + +Node::~Node() +{ + if (d) D(d); +} + +bool Node::isNull() const +{ + return d == 0; +} + +v8::Handle<v8::Value> NamedNodeMap::length(v8::Local<v8::String>, const v8::AccessorInfo &args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + Q_UNUSED(engine) + return v8::Integer::New(r->list->count()); +} + +v8::Handle<v8::Value> NamedNodeMap::indexed(uint32_t index, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || !r->list) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if ((int)index < r->list->count()) { + return Node::create(engine, r->list->at(index)); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> NamedNodeMap::named(v8::Local<v8::String> property, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || !r->list) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + QString str = engine->toString(property); + for (int ii = 0; ii < r->list->count(); ++ii) { + if (r->list->at(ii)->name == str) { + return Node::create(engine, r->list->at(ii)); + } + } + + return v8::Undefined(); +} + +v8::Handle<v8::Object> NamedNodeMap::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->namedNodeMapPrototype.IsEmpty()) { + v8::Local<v8::ObjectTemplate> ot = v8::ObjectTemplate::New(); + ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::Wrap(engine)); + ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine)); + ot->SetFallbackPropertyHandler(named, 0, 0, 0, 0, v8::External::Wrap(engine)); + d->namedNodeMapPrototype = qPersistentNew<v8::Object>(ot->NewInstance()); + engine->freezeObject(d->namedNodeMapPrototype); + } + return d->namedNodeMapPrototype; +} + +v8::Handle<v8::Value> NamedNodeMap::create(QV8Engine *engine, NodeImpl *data, QList<NodeImpl *> *list) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + v8::Local<v8::Object> instance = d->newNode(); + instance->SetPrototype(NamedNodeMap::prototype(engine)); + QQmlDOMNodeResource *r = new QQmlDOMNodeResource(engine); + r->d = data; + r->list = list; + if (data) A(data); + instance->SetExternalResource(r); + return instance; +} + +v8::Handle<v8::Value> NodeList::indexed(uint32_t index, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + if ((int)index < r->d->children.count()) { + return Node::create(engine, r->d->children.at(index)); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> NodeList::length(v8::Local<v8::String>, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + Q_UNUSED(engine) + return v8::Integer::New(r->d->children.count()); +} + +v8::Handle<v8::Object> NodeList::prototype(QV8Engine *engine) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + if (d->nodeListPrototype.IsEmpty()) { + v8::Local<v8::ObjectTemplate> ot = v8::ObjectTemplate::New(); + ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::Wrap(engine)); + ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine)); + d->nodeListPrototype = qPersistentNew<v8::Object>(ot->NewInstance()); + engine->freezeObject(d->nodeListPrototype); + } + return d->nodeListPrototype; +} + +v8::Handle<v8::Value> NodeList::create(QV8Engine *engine, NodeImpl *data) +{ + QQmlXMLHttpRequestData *d = xhrdata(engine); + v8::Local<v8::Object> instance = d->newNode(); + instance->SetPrototype(NodeList::prototype(engine)); + QQmlDOMNodeResource *r = new QQmlDOMNodeResource(engine); + r->d = data; + if (data) A(data); + instance->SetExternalResource(r); + return instance; +} + +v8::Handle<v8::Value> Document::documentElement(v8::Local<v8::String>, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || r->d->type != NodeImpl::Document) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return Node::create(engine, static_cast<DocumentImpl *>(r->d)->root); +} + +v8::Handle<v8::Value> Document::xmlStandalone(v8::Local<v8::String>, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || r->d->type != NodeImpl::Document) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + Q_UNUSED(engine) + return v8::Boolean::New(static_cast<DocumentImpl *>(r->d)->isStandalone); +} + +v8::Handle<v8::Value> Document::xmlVersion(v8::Local<v8::String>, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || r->d->type != NodeImpl::Document) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return engine->toString(static_cast<DocumentImpl *>(r->d)->version); +} + +v8::Handle<v8::Value> Document::xmlEncoding(v8::Local<v8::String>, const v8::AccessorInfo& args) +{ + QQmlDOMNodeResource *r = v8_resource_cast<QQmlDOMNodeResource>(args.This()); + if (!r || r->d->type != NodeImpl::Document) return v8::Undefined(); + QV8Engine *engine = V8ENGINE(); + + return engine->toString(static_cast<DocumentImpl *>(r->d)->encoding); +} + +class QQmlXMLHttpRequest : public QObject, public QV8ObjectResource +{ +Q_OBJECT +V8_RESOURCE_TYPE(XMLHttpRequestType) +public: + enum State { Unsent = 0, + Opened = 1, HeadersReceived = 2, + Loading = 3, Done = 4 }; + + QQmlXMLHttpRequest(QV8Engine *engine, QNetworkAccessManager *manager); + virtual ~QQmlXMLHttpRequest(); + + bool sendFlag() const; + bool errorFlag() const; + quint32 readyState() const; + int replyStatus() const; + QString replyStatusText() const; + + v8::Handle<v8::Value> open(v8::Handle<v8::Object> me, const QString &, const QUrl &); + v8::Handle<v8::Value> send(v8::Handle<v8::Object> me, const QByteArray &); + v8::Handle<v8::Value> abort(v8::Handle<v8::Object> me); + + void addHeader(const QString &, const QString &); + QString header(const QString &name); + QString headers(); + + + QString responseBody(); + const QByteArray & rawResponseBody() const; + bool receivedXml() const; +private slots: + void downloadProgress(qint64); + void error(QNetworkReply::NetworkError); + void finished(); + +private: + void requestFromUrl(const QUrl &url); + + State m_state; + bool m_errorFlag; + bool m_sendFlag; + QString m_method; + QUrl m_url; + QByteArray m_responseEntityBody; + QByteArray m_data; + int m_redirectCount; + + typedef QPair<QByteArray, QByteArray> HeaderPair; + typedef QList<HeaderPair> HeadersList; + HeadersList m_headersList; + void fillHeadersList(); + + bool m_gotXml; + QByteArray m_mime; + QByteArray m_charset; + QTextCodec *m_textCodec; +#ifndef QT_NO_TEXTCODEC + QTextCodec* findTextCodec() const; +#endif + void readEncoding(); + + v8::Handle<v8::Object> getMe() const; + void setMe(v8::Handle<v8::Object> me); + v8::Persistent<v8::Object> m_me; + + void dispatchCallback(v8::Handle<v8::Object> me); + void printError(v8::Handle<v8::Message>); + + int m_status; + QString m_statusText; + QNetworkRequest m_request; + QQmlGuard<QNetworkReply> m_network; + void destroyNetwork(); + + QNetworkAccessManager *m_nam; + QNetworkAccessManager *networkAccessManager() { return m_nam; } +}; + +QQmlXMLHttpRequest::QQmlXMLHttpRequest(QV8Engine *engine, QNetworkAccessManager *manager) +: QV8ObjectResource(engine), m_state(Unsent), m_errorFlag(false), m_sendFlag(false), + m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) +{ +} + +QQmlXMLHttpRequest::~QQmlXMLHttpRequest() +{ + destroyNetwork(); +} + +bool QQmlXMLHttpRequest::sendFlag() const +{ + return m_sendFlag; +} + +bool QQmlXMLHttpRequest::errorFlag() const +{ + return m_errorFlag; +} + +quint32 QQmlXMLHttpRequest::readyState() const +{ + return m_state; +} + +int QQmlXMLHttpRequest::replyStatus() const +{ + return m_status; +} + +QString QQmlXMLHttpRequest::replyStatusText() const +{ + return m_statusText; +} + +v8::Handle<v8::Value> QQmlXMLHttpRequest::open(v8::Handle<v8::Object> me, const QString &method, + const QUrl &url) +{ + destroyNetwork(); + m_sendFlag = false; + m_errorFlag = false; + m_responseEntityBody = QByteArray(); + m_method = method; + m_url = url; + m_state = Opened; + dispatchCallback(me); + return v8::Undefined(); +} + +void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value) +{ + QByteArray utfname = name.toUtf8(); + + if (m_request.hasRawHeader(utfname)) { + m_request.setRawHeader(utfname, m_request.rawHeader(utfname) + ',' + value.toUtf8()); + } else { + m_request.setRawHeader(utfname, value.toUtf8()); + } +} + +QString QQmlXMLHttpRequest::header(const QString &name) +{ + QByteArray utfname = name.toLower().toUtf8(); + + foreach (const HeaderPair &header, m_headersList) { + if (header.first == utfname) + return QString::fromUtf8(header.second); + } + return QString(); +} + +QString QQmlXMLHttpRequest::headers() +{ + QString ret; + + foreach (const HeaderPair &header, m_headersList) { + if (ret.length()) + ret.append(QLatin1String("\r\n")); + ret = ret % QString::fromUtf8(header.first) % QLatin1String(": ") + % QString::fromUtf8(header.second); + } + return ret; +} + +void QQmlXMLHttpRequest::fillHeadersList() +{ + QList<QByteArray> headerList = m_network->rawHeaderList(); + + m_headersList.clear(); + foreach (const QByteArray &header, headerList) { + HeaderPair pair (header.toLower(), m_network->rawHeader(header)); + if (pair.first == "set-cookie" || + pair.first == "set-cookie2") + continue; + + m_headersList << pair; + } +} + +void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) +{ + QNetworkRequest request = m_request; + request.setUrl(url); + if(m_method == QLatin1String("POST") || + m_method == QLatin1String("PUT")) { + QVariant var = request.header(QNetworkRequest::ContentTypeHeader); + if (var.isValid()) { + QString str = var.toString(); + int charsetIdx = str.indexOf(QLatin1String("charset=")); + if (charsetIdx == -1) { + // No charset - append + if (!str.isEmpty()) str.append(QLatin1Char(';')); + str.append(QLatin1String("charset=UTF-8")); + } else { + charsetIdx += 8; + int n = 0; + int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx); + if (semiColon == -1) { + n = str.length() - charsetIdx; + } else { + n = semiColon - charsetIdx; + } + + str.replace(charsetIdx, n, QLatin1String("UTF-8")); + } + request.setHeader(QNetworkRequest::ContentTypeHeader, str); + } else { + request.setHeader(QNetworkRequest::ContentTypeHeader, + QLatin1String("text/plain;charset=UTF-8")); + } + } + + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: " << qPrintable(m_method) << " " << qPrintable(url.toString()); + if (!m_data.isEmpty()) { + qWarning().nospace() << " " + << qPrintable(QString::fromUtf8(m_data)); + } + } + + if (m_method == QLatin1String("GET")) + m_network = networkAccessManager()->get(request); + else if (m_method == QLatin1String("HEAD")) + m_network = networkAccessManager()->head(request); + else if (m_method == QLatin1String("POST")) + m_network = networkAccessManager()->post(request, m_data); + else if (m_method == QLatin1String("PUT")) + m_network = networkAccessManager()->put(request, m_data); + else if (m_method == QLatin1String("DELETE")) + m_network = networkAccessManager()->deleteResource(request); + + QObject::connect(m_network, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(downloadProgress(qint64))); + QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + QObject::connect(m_network, SIGNAL(finished()), + this, SLOT(finished())); +} + +v8::Handle<v8::Value> QQmlXMLHttpRequest::send(v8::Handle<v8::Object> me, const QByteArray &data) +{ + m_errorFlag = false; + m_sendFlag = true; + m_redirectCount = 0; + m_data = data; + + setMe(me); + + requestFromUrl(m_url); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QQmlXMLHttpRequest::abort(v8::Handle<v8::Object> me) +{ + destroyNetwork(); + m_responseEntityBody = QByteArray(); + m_errorFlag = true; + m_request = QNetworkRequest(); + + if (!(m_state == Unsent || + (m_state == Opened && !m_sendFlag) || + m_state == Done)) { + + m_state = Done; + m_sendFlag = false; + dispatchCallback(me); + } + + m_state = Unsent; + + return v8::Undefined(); +} + +v8::Handle<v8::Object> QQmlXMLHttpRequest::getMe() const +{ + return m_me; +} + +void QQmlXMLHttpRequest::setMe(v8::Handle<v8::Object> me) +{ + qPersistentDispose(m_me); + + if (!me.IsEmpty()) + m_me = qPersistentNew<v8::Object>(me); +} + +void QQmlXMLHttpRequest::downloadProgress(qint64 bytes) +{ + v8::HandleScope handle_scope; + + Q_UNUSED(bytes) + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + // ### We assume if this is called the headers are now available + if (m_state < HeadersReceived) { + m_state = HeadersReceived; + fillHeadersList (); + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + } + + bool wasEmpty = m_responseEntityBody.isEmpty(); + m_responseEntityBody.append(m_network->readAll()); + if (wasEmpty && !m_responseEntityBody.isEmpty()) { + m_state = Loading; + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + } +} + +static const char *errorToString(QNetworkReply::NetworkError error) +{ + int idx = QNetworkReply::staticMetaObject.indexOfEnumerator("NetworkError"); + if (idx == -1) return "EnumLookupFailed"; + + QMetaEnum e = QNetworkReply::staticMetaObject.enumerator(idx); + + const char *name = e.valueToKey(error); + if (!name) return "EnumLookupFailed"; + else return name; +} + +void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error) +{ + v8::HandleScope handle_scope; + + Q_UNUSED(error) + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + m_responseEntityBody = QByteArray(); + + m_request = QNetworkRequest(); + m_data.clear(); + destroyNetwork(); + + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: ERROR " << qPrintable(m_url.toString()); + qWarning().nospace() << " " << error << " " << errorToString(error) << " " << m_statusText; + } + + if (error == QNetworkReply::ContentAccessDenied || + error == QNetworkReply::ContentOperationNotPermittedError || + error == QNetworkReply::ContentNotFoundError || + error == QNetworkReply::AuthenticationRequiredError || + error == QNetworkReply::ContentReSendError || + error == QNetworkReply::UnknownContentError) { + m_state = Loading; + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + } else { + m_errorFlag = true; + } + + m_state = Done; + + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); +} + +#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 +void QQmlXMLHttpRequest::finished() +{ + v8::HandleScope handle_scope; + + m_redirectCount++; + if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = m_network->url().resolved(redirect.toUrl()); + destroyNetwork(); + requestFromUrl(url); + return; + } + } + + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + if (m_state < HeadersReceived) { + m_state = HeadersReceived; + fillHeadersList (); + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + } + m_responseEntityBody.append(m_network->readAll()); + readEncoding(); + + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: RESPONSE " << qPrintable(m_url.toString()); + if (!m_responseEntityBody.isEmpty()) { + qWarning().nospace() << " " + << qPrintable(QString::fromUtf8(m_responseEntityBody)); + } + } + + + m_data.clear(); + destroyNetwork(); + if (m_state < Loading) { + m_state = Loading; + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + } + m_state = Done; + + v8::TryCatch tc; + dispatchCallback(m_me); + if (tc.HasCaught()) printError(tc.Message()); + + setMe(v8::Handle<v8::Object>()); +} + + +void QQmlXMLHttpRequest::readEncoding() +{ + foreach (const HeaderPair &header, m_headersList) { + if (header.first == "content-type") { + int separatorIdx = header.second.indexOf(';'); + if (separatorIdx == -1) { + m_mime == header.second; + } else { + m_mime = header.second.mid(0, separatorIdx); + int charsetIdx = header.second.indexOf("charset="); + if (charsetIdx != -1) { + charsetIdx += 8; + separatorIdx = header.second.indexOf(';', charsetIdx); + m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.length()); + } + } + break; + } + } + + if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml")) + m_gotXml = true; +} + +bool QQmlXMLHttpRequest::receivedXml() const +{ + return m_gotXml; +} + + +#ifndef QT_NO_TEXTCODEC +QTextCodec* QQmlXMLHttpRequest::findTextCodec() const +{ + QTextCodec *codec = 0; + + if (!m_charset.isEmpty()) + codec = QTextCodec::codecForName(m_charset); + + if (!codec && m_gotXml) { + QXmlStreamReader reader(m_responseEntityBody); + reader.readNext(); + codec = QTextCodec::codecForName(reader.documentEncoding().toString().toUtf8()); + } + + if (!codec && m_mime == "text/html") + codec = QTextCodec::codecForHtml(m_responseEntityBody, 0); + + if (!codec) + codec = QTextCodec::codecForUtfText(m_responseEntityBody, 0); + + if (!codec) + codec = QTextCodec::codecForName("UTF-8"); + return codec; +} +#endif + + +QString QQmlXMLHttpRequest::responseBody() +{ +#ifndef QT_NO_TEXTCODEC + if (!m_textCodec) + m_textCodec = findTextCodec(); + if (m_textCodec) + return m_textCodec->toUnicode(m_responseEntityBody); +#endif + + return QString::fromUtf8(m_responseEntityBody); +} + +const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const +{ + return m_responseEntityBody; +} + +// Requires a TryCatch scope +void QQmlXMLHttpRequest::dispatchCallback(v8::Handle<v8::Object> me) +{ + v8::Local<v8::Value> callback = me->Get(v8::String::New("onreadystatechange")); + if (callback->IsFunction()) { + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback); + + f->Call(me, 0, 0); + } +} + +// Must have a handle scope +void QQmlXMLHttpRequest::printError(v8::Handle<v8::Message> message) +{ + v8::Context::Scope scope(engine->context()); + + QQmlError error; + QQmlExpressionPrivate::exceptionToError(message, error); + QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()), error); +} + +void QQmlXMLHttpRequest::destroyNetwork() +{ + if (m_network) { + m_network->disconnect(); + m_network->deleteLater(); + m_network = 0; + } +} + +// XMLHttpRequest methods +static v8::Handle<v8::Value> qmlxmlhttprequest_open(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (args.Length() < 2 || args.Length() > 5) + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + + QV8Engine *engine = r->engine; + + // Argument 0 - Method + QString method = engine->toString(args[0]).toUpper(); + if (method != QLatin1String("GET") && + method != QLatin1String("PUT") && + method != QLatin1String("HEAD") && + method != QLatin1String("POST") && + method != QLatin1String("DELETE")) + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); + + // Argument 1 - URL + QUrl url = QUrl::fromEncoded(engine->toString(args[1]).toUtf8()); + + if (url.isRelative()) + url = engine->callingContext()->resolvedUrl(url); + + // Argument 2 - async (optional) + if (args.Length() > 2 && !args[2]->BooleanValue()) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Synchronous XMLHttpRequest calls are not supported"); + + // Argument 3/4 - user/pass (optional) + QString username, password; + if (args.Length() > 3) + username = engine->toString(args[3]); + if (args.Length() > 4) + password = engine->toString(args[4]); + + // Clear the fragment (if any) + url.setFragment(QString()); + + // Set username/password + if (!username.isNull()) url.setUserName(username); + if (!password.isNull()) url.setPassword(password); + + return r->open(args.This(), method, url); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_setRequestHeader(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (args.Length() != 2) + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + + if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag()) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + QV8Engine *engine = r->engine; + + QString name = engine->toString(args[0]); + QString value = engine->toString(args[1]); + + // ### Check that name and value are well formed + + QString nameUpper = name.toUpper(); + if (nameUpper == QLatin1String("ACCEPT-CHARSET") || + nameUpper == QLatin1String("ACCEPT-ENCODING") || + nameUpper == QLatin1String("CONNECTION") || + nameUpper == QLatin1String("CONTENT-LENGTH") || + nameUpper == QLatin1String("COOKIE") || + nameUpper == QLatin1String("COOKIE2") || + nameUpper == QLatin1String("CONTENT-TRANSFER-ENCODING") || + nameUpper == QLatin1String("DATE") || + nameUpper == QLatin1String("EXPECT") || + nameUpper == QLatin1String("HOST") || + nameUpper == QLatin1String("KEEP-ALIVE") || + nameUpper == QLatin1String("REFERER") || + nameUpper == QLatin1String("TE") || + nameUpper == QLatin1String("TRAILER") || + nameUpper == QLatin1String("TRANSFER-ENCODING") || + nameUpper == QLatin1String("UPGRADE") || + nameUpper == QLatin1String("USER-AGENT") || + nameUpper == QLatin1String("VIA") || + nameUpper.startsWith(QLatin1String("PROXY-")) || + nameUpper.startsWith(QLatin1String("SEC-"))) + return v8::Undefined(); + + r->addHeader(name, value); + + return v8::Undefined(); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_send(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + QV8Engine *engine = r->engine; + + if (r->readyState() != QQmlXMLHttpRequest::Opened || + r->sendFlag()) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + QByteArray data; + if (args.Length() > 0) + data = engine->toString(args[0]).toUtf8(); + + return r->send(args.This(), data); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_abort(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + return r->abort(args.This()); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_getResponseHeader(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + QV8Engine *engine = r->engine; + + if (args.Length() != 1) + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + + if (r->readyState() != QQmlXMLHttpRequest::Loading && + r->readyState() != QQmlXMLHttpRequest::Done && + r->readyState() != QQmlXMLHttpRequest::HeadersReceived) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + return engine->toString(r->header(engine->toString(args[0]))); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_getAllResponseHeaders(const v8::Arguments &args) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(args.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + QV8Engine *engine = r->engine; + + if (args.Length() != 0) + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + + if (r->readyState() != QQmlXMLHttpRequest::Loading && + r->readyState() != QQmlXMLHttpRequest::Done && + r->readyState() != QQmlXMLHttpRequest::HeadersReceived) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + return engine->toString(r->headers()); +} + +// XMLHttpRequest properties +static v8::Handle<v8::Value> qmlxmlhttprequest_readyState(v8::Local<v8::String> /* property */, + const v8::AccessorInfo& info) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(info.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + return v8::Integer::NewFromUnsigned(r->readyState()); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_status(v8::Local<v8::String> /* property */, + const v8::AccessorInfo& info) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(info.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (r->readyState() == QQmlXMLHttpRequest::Unsent || + r->readyState() == QQmlXMLHttpRequest::Opened) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + if (r->errorFlag()) + return v8::Integer::New(0); + else + return v8::Integer::New(r->replyStatus()); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_statusText(v8::Local<v8::String> /* property */, + const v8::AccessorInfo& info) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(info.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + QV8Engine *engine = r->engine; + + if (r->readyState() == QQmlXMLHttpRequest::Unsent || + r->readyState() == QQmlXMLHttpRequest::Opened) + V8THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); + + if (r->errorFlag()) + return engine->toString(QString()); + else + return engine->toString(r->replyStatusText()); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_responseText(v8::Local<v8::String> /* property */, + const v8::AccessorInfo& info) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(info.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + QV8Engine *engine = r->engine; + + if (r->readyState() != QQmlXMLHttpRequest::Loading && + r->readyState() != QQmlXMLHttpRequest::Done) + return engine->toString(QString()); + else + return engine->toString(r->responseBody()); +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_responseXML(v8::Local<v8::String> /* property */, + const v8::AccessorInfo& info) +{ + QQmlXMLHttpRequest *r = v8_resource_cast<QQmlXMLHttpRequest>(info.This()); + if (!r) + V8THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (!r->receivedXml() || + (r->readyState() != QQmlXMLHttpRequest::Loading && + r->readyState() != QQmlXMLHttpRequest::Done)) { + return v8::Null(); + } else { + return Document::load(r->engine, r->rawResponseBody()); + } +} + +static v8::Handle<v8::Value> qmlxmlhttprequest_new(const v8::Arguments &args) +{ + if (args.IsConstructCall()) { + QV8Engine *engine = V8ENGINE(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + Q_UNUSED(ep) + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(engine, engine->networkAccessManager()); + args.This()->SetExternalResource(r); + + return args.This(); + } else { + return v8::Undefined(); + } +} + +#define NEWFUNCTION(function) v8::FunctionTemplate::New(function)->GetFunction() + +void qt_rem_qmlxmlhttprequest(QV8Engine * /* engine */, void *d) +{ + QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d; + delete data; +} + +void *qt_add_qmlxmlhttprequest(QV8Engine *engine) +{ + v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + + // XMLHttpRequest + v8::Local<v8::FunctionTemplate> xmlhttprequest = v8::FunctionTemplate::New(qmlxmlhttprequest_new, + v8::External::Wrap(engine)); + xmlhttprequest->InstanceTemplate()->SetHasExternalResource(true); + + // Methods + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("open"), NEWFUNCTION(qmlxmlhttprequest_open), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("setRequestHeader"), NEWFUNCTION(qmlxmlhttprequest_setRequestHeader), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("send"), NEWFUNCTION(qmlxmlhttprequest_send), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("abort"), NEWFUNCTION(qmlxmlhttprequest_abort), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("getResponseHeader"), NEWFUNCTION(qmlxmlhttprequest_getResponseHeader), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("getAllResponseHeaders"), NEWFUNCTION(qmlxmlhttprequest_getAllResponseHeaders), attributes); + + // Read-only properties + xmlhttprequest->PrototypeTemplate()->SetAccessor(v8::String::New("readyState"), qmlxmlhttprequest_readyState, 0, v8::Handle<v8::Value>(), v8::DEFAULT, attributes); + xmlhttprequest->PrototypeTemplate()->SetAccessor(v8::String::New("status"),qmlxmlhttprequest_status, 0, v8::Handle<v8::Value>(), v8::DEFAULT, attributes); + xmlhttprequest->PrototypeTemplate()->SetAccessor(v8::String::New("statusText"),qmlxmlhttprequest_statusText, 0, v8::Handle<v8::Value>(), v8::DEFAULT, attributes); + xmlhttprequest->PrototypeTemplate()->SetAccessor(v8::String::New("responseText"),qmlxmlhttprequest_responseText, 0, v8::Handle<v8::Value>(), v8::DEFAULT, attributes); + xmlhttprequest->PrototypeTemplate()->SetAccessor(v8::String::New("responseXML"),qmlxmlhttprequest_responseXML, 0, v8::Handle<v8::Value>(), v8::DEFAULT, attributes); + + // State values + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("UNSENT"), v8::Integer::New(0), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("OPENED"), v8::Integer::New(1), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("HEADERS_RECEIVED"), v8::Integer::New(2), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("LOADING"), v8::Integer::New(3), attributes); + xmlhttprequest->PrototypeTemplate()->Set(v8::String::New("DONE"), v8::Integer::New(4), attributes); + + // Constructor + xmlhttprequest->Set(v8::String::New("UNSENT"), v8::Integer::New(0), attributes); + xmlhttprequest->Set(v8::String::New("OPENED"), v8::Integer::New(1), attributes); + xmlhttprequest->Set(v8::String::New("HEADERS_RECEIVED"), v8::Integer::New(2), attributes); + xmlhttprequest->Set(v8::String::New("LOADING"), v8::Integer::New(3), attributes); + xmlhttprequest->Set(v8::String::New("DONE"), v8::Integer::New(4), attributes); + engine->global()->Set(v8::String::New("XMLHttpRequest"), xmlhttprequest->GetFunction()); + + QQmlXMLHttpRequestData *data = new QQmlXMLHttpRequestData; + return data; +} + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAMREADER + +#include <qqmlxmlhttprequest.moc> diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h new file mode 100644 index 0000000000..2cfdf6a140 --- /dev/null +++ b/src/qml/qml/qqmlxmlhttprequest_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLXMLHTTPREQUEST_P_H +#define QQMLXMLHTTPREQUEST_P_H + +#include <QtQml/qjsengine.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> + +#ifndef QT_NO_XMLSTREAMREADER + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +void *qt_add_qmlxmlhttprequest(QV8Engine *engine); +void qt_rem_qmlxmlhttprequest(QV8Engine *engine, void *); + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAMREADER + +#endif // QQMLXMLHTTPREQUEST_P_H + diff --git a/src/qml/qml/qquickapplication.cpp b/src/qml/qml/qquickapplication.cpp new file mode 100644 index 0000000000..198a917cb8 --- /dev/null +++ b/src/qml/qml/qquickapplication.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickapplication_p.h" +#include <private/qobject_p.h> +#include <QtGui/QGuiApplication> +#include <QtGui/QInputMethod> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +class QQuickApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickApplication) +public: + QQuickApplicationPrivate() : active(QGuiApplication::activeWindow() != 0), + layoutDirection(QGuiApplication::layoutDirection()) {} + bool active; + Qt::LayoutDirection layoutDirection; +}; + +/* + This object and its properties are documented as part of the Qt object, + in qdeclarativengine.cpp +*/ + +QQuickApplication::QQuickApplication(QObject *parent) : QObject(*new QQuickApplicationPrivate(), parent) +{ + if (qApp) + qApp->installEventFilter(this); +} + +QQuickApplication::~QQuickApplication() +{ +} + +bool QQuickApplication::active() const +{ + Q_D(const QQuickApplication); + return d->active; +} + +Qt::LayoutDirection QQuickApplication::layoutDirection() const +{ + Q_D(const QQuickApplication); + return d->layoutDirection; +} + +QObject *QQuickApplication::inputPanel() const +{ + static bool warned = false; + if (!warned) { + qWarning() << "Qt.application.inputPanel is deprecated, use Qt.inputMethod instead"; + warned = true; + } + return qApp ? qApp->inputMethod() : 0; +} + +bool QQuickApplication::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_D(QQuickApplication); + if (event->type() == QEvent::ApplicationActivate + || event->type() == QEvent::ApplicationDeactivate) { + bool active = d->active; + if (event->type() == QEvent::ApplicationActivate) + active = true; + else if (event->type() == QEvent::ApplicationDeactivate) + active = false; + + if (d->active != active) { + d->active = active; + emit activeChanged(); + } + } + if (event->type() == QEvent::LayoutDirectionChange) { + Qt::LayoutDirection direction = QGuiApplication::layoutDirection(); + if (d->layoutDirection != direction) { + d->layoutDirection = direction; + emit layoutDirectionChanged(); + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qquickapplication_p.h b/src/qml/qml/qquickapplication_p.h new file mode 100644 index 0000000000..66198948ac --- /dev/null +++ b/src/qml/qml/qquickapplication_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKAPPLICATION_P_H +#define QQUICKAPPLICATION_P_H + +#include <QtCore/QObject> +#include <qqml.h> +#include <private/qtqmlglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQuickApplicationPrivate; +class Q_QML_PRIVATE_EXPORT QQuickApplication : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(QObject *inputPanel READ inputPanel CONSTANT) + +public: + explicit QQuickApplication(QObject *parent = 0); + virtual ~QQuickApplication(); + bool active() const; + Qt::LayoutDirection layoutDirection() const; + QT_DEPRECATED QObject *inputPanel() const; + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +Q_SIGNALS: + void activeChanged(); + void layoutDirectionChanged(); + +private: + Q_DISABLE_COPY(QQuickApplication) + Q_DECLARE_PRIVATE(QQuickApplication) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickApplication) + +QT_END_HEADER + +#endif // QQUICKAPPLICATION_P_H diff --git a/src/qml/qml/qquicklistmodel.cpp b/src/qml/qml/qquicklistmodel.cpp new file mode 100644 index 0000000000..30c4f5d1d1 --- /dev/null +++ b/src/qml/qml/qquicklistmodel.cpp @@ -0,0 +1,2467 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklistmodel_p_p.h" +#include "qquicklistmodelworkeragent_p.h" +#include "qqmlopenmetaobject_p.h" +#include <private/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> + +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlscript_p.h> +#include <private/qqmlengine_p.h> +#include <qqmlcontext.h> +#include <qqmlinfo.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstack.h> +#include <QXmlStreamReader> + +Q_DECLARE_METATYPE(QListModelInterface *) + +QT_BEGIN_NAMESPACE + +// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. +enum { MIN_LISTMODEL_UID = 1024 }; + +static QAtomicInt uidCounter(MIN_LISTMODEL_UID); + +template <typename T> +static bool isMemoryUsed(const char *mem) +{ + for (size_t i=0 ; i < sizeof(T) ; ++i) { + if (mem[i] != 0) + return true; + } + + return false; +} + +static QString roleTypeName(ListLayout::Role::DataType t) +{ + QString result; + const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap" }; + + if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) + result = QString::fromLatin1(roleTypeNames[t]); + + return result; +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) +{ + QStringHash<Role *>::Node *node = roleHash.findNode(key); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + return createRole(key, type); +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type) +{ + QHashedV8String hashedKey(key); + QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + QString qkey; + qkey.resize(key->Length()); + key->Write(reinterpret_cast<uint16_t*>(qkey.data())); + + return createRole(qkey, type); +} + +const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) +{ + const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard<QObject>), sizeof(QVariantMap) }; + const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap) }; + + Role *r = new Role; + r->name = key; + r->type = type; + + if (type == Role::List) { + r->subLayout = new ListLayout; + } else { + r->subLayout = 0; + } + + int dataSize = dataSizes[type]; + int dataAlignment = dataAlignments[type]; + + int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); + if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { + r->blockIndex = ++currentBlock; + r->blockOffset = 0; + currentBlockOffset = dataSize; + } else { + r->blockIndex = currentBlock; + r->blockOffset = dataOffset; + currentBlockOffset = dataOffset + dataSize; + } + + int roleIndex = roles.count(); + r->index = roleIndex; + + roles.append(r); + roleHash.insert(key, r); + + return *r; +} + +ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) +{ + for (int i=0 ; i < other->roles.count() ; ++i) { + Role *role = new Role(other->roles[i]); + roles.append(role); + roleHash.insert(role->name, role); + } + currentBlockOffset = other->currentBlockOffset; + currentBlock = other->currentBlock; +} + +ListLayout::~ListLayout() +{ + for (int i=0 ; i < roles.count() ; ++i) { + delete roles[i]; + } +} + +void ListLayout::sync(ListLayout *src, ListLayout *target) +{ + int roleOffset = target->roles.count(); + int newRoleCount = src->roles.count() - roleOffset; + + for (int i=0 ; i < newRoleCount ; ++i) { + Role *role = new Role(src->roles[roleOffset + i]); + target->roles.append(role); + target->roleHash.insert(role->name, role); + } + + target->currentBlockOffset = src->currentBlockOffset; + target->currentBlock = src->currentBlock; +} + +ListLayout::Role::Role(const Role *other) +{ + name = other->name; + type = other->type; + blockIndex = other->blockIndex; + blockOffset = other->blockOffset; + index = other->index; + if (other->subLayout) + subLayout = new ListLayout(other->subLayout); + else + subLayout = 0; +} + +ListLayout::Role::~Role() +{ + delete subLayout; +} + +const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) +{ + Role::DataType type; + + switch (data.type()) { + case QVariant::Double: type = Role::Number; break; + case QVariant::Int: type = Role::Number; break; + case QVariant::UserType: type = Role::List; break; + case QVariant::Bool: type = Role::Bool; break; + case QVariant::String: type = Role::String; break; + case QVariant::Map: type = Role::VariantMap; break; + default: type = Role::Invalid; break; + } + + if (type == Role::Invalid) { + qmlInfo(0) << "Can't create role for unsupported data type"; + return 0; + } + + return &getRoleOrCreate(key, type); +} + +const ListLayout::Role *ListLayout::getExistingRole(const QString &key) +{ + Role *r = 0; + QStringHash<Role *>::Node *node = roleHash.findNode(key); + if (node) + r = node->value; + return r; +} + +const ListLayout::Role *ListLayout::getExistingRole(v8::Handle<v8::String> key) +{ + Role *r = 0; + QHashedV8String hashedKey(key); + QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey); + if (node) + r = node->value; + return r; +} + +ModelObject *ListModel::getOrCreateModelObject(QQuickListModel *model, int elementIndex) +{ + ListElement *e = elements[elementIndex]; + if (e->m_objectCache == 0) { + e->m_objectCache = new ModelObject(model, elementIndex); + } + return e->m_objectCache; +} + +void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash) +{ + // Sanity check + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + + // Build hash of elements <-> uid for each of the lists + QHash<int, ElementSync> elementHash; + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *e = src->elements.at(i); + int uid = e->getUid(); + + QHash<int, ElementSync>::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash<int, ElementSync>::iterator it = elementHash.begin(); + QHash<int, ElementSync>::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + s.target->destroy(target->m_layout); + target->elements.removeOne(s.target); + delete s.target; + } + ++it; + } + + // Sync the layouts + ListLayout::sync(src->m_layout, target->m_layout); + + // Clear the target list, and append in correct order from the source + target->elements.clear(); + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *srcElement = src->elements.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + ListElement *targetElement = s.target; + if (targetElement == 0) { + targetElement = new ListElement(srcElement->getUid()); + } + ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); + target->elements.append(targetElement); + } + + target->updateCacheIndices(); + + // Update values stored in target meta objects + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements[i]; + if (e->m_objectCache) + e->m_objectCache->updateValues(); + } +} + +ListModel::ListModel(ListLayout *layout, QQuickListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) +{ + if (uid == -1) + uid = uidCounter.fetchAndAddOrdered(1); + m_uid = uid; +} + +void ListModel::destroy() +{ + clear(); + m_uid = -1; + m_layout = 0; + if (m_modelCache && m_modelCache->m_primary == false) + delete m_modelCache; + m_modelCache = 0; +} + +int ListModel::appendElement() +{ + int elementIndex = elements.count(); + newElement(elementIndex); + return elementIndex; +} + +void ListModel::insertElement(int index) +{ + newElement(index); + updateCacheIndices(); +} + +void ListModel::move(int from, int to, int n) +{ + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector<ListElement *, 4> store; + for (int i=0 ; i < (to-from) ; ++i) + store.append(elements[from+n+i]); + for (int i=0 ; i < n ; ++i) + store.append(elements[from+i]); + for (int i=0 ; i < store.count() ; ++i) + elements[from+i] = store[i]; + + updateCacheIndices(); +} + +void ListModel::newElement(int index) +{ + ListElement *e = new ListElement; + elements.insert(index, e); +} + +void ListModel::updateCacheIndices() +{ + for (int i=0 ; i < elements.count() ; ++i) { + ListElement *e = elements.at(i); + if (e->m_objectCache) { + e->m_objectCache->m_elementIndex = i; + } + } +} + +QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQuickListModel *owner, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); + return e->getProperty(r, owner, eng); +} + +ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) +{ + ListElement *e = elements[elementIndex]; + return e->getListProperty(role); +} + +void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QList<int> *roles, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local<v8::Array> propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString(); + v8::Local<v8::Value> propertyValue = object->Get(propertyName); + + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + v8::Handle<v8::String> jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + roleIndex = e->setStringProperty(r, qstr); + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue()); + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + roleIndex = e->setListProperty(r, subModel); + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (role.type == ListLayout::Role::QObject) + roleIndex = e->setQObjectProperty(role, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + + if (roleIndex != -1) + roles->append(roleIndex); + } + + if (e->m_objectCache) { + e->m_objectCache->updateValues(*roles); + } +} + +void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local<v8::Array> propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString(); + v8::Local<v8::Value> propertyValue = object->Get(propertyName); + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + if (r.type == ListLayout::Role::String) { + v8::Handle<v8::String> jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + e->setStringPropertyFast(r, qstr); + } + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + if (r.type == ListLayout::Role::Number) { + e->setDoublePropertyFast(r, propertyValue->NumberValue()); + } + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + if (r.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + e->setListPropertyFast(r, subModel); + } + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + if (r.type == ListLayout::Role::Bool) { + e->setBoolPropertyFast(r, propertyValue->BooleanValue()); + } + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (r.type == ListLayout::Role::QObject) + e->setQObjectPropertyFast(r, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + e->setVariantMapFast(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + } +} + +void ListModel::clear() +{ + int elementCount = elements.count(); + for (int i=0 ; i < elementCount ; ++i) { + elements[i]->destroy(m_layout); + delete elements[i]; + } + elements.clear(); +} + +void ListModel::remove(int index, int count) +{ + for (int i=0 ; i < count ; ++i) { + elements[index+i]->destroy(m_layout); + delete elements[index+i]; + } + elements.remove(index, count); + updateCacheIndices(); +} + +void ListModel::insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng) +{ + insertElement(elementIndex); + set(elementIndex, object, eng); +} + +int ListModel::append(v8::Handle<v8::Object> object, QV8Engine *eng) +{ + int elementIndex = appendElement(); + set(elementIndex, object, eng); + return elementIndex; +} + +int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + + const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); + if (r) { + roleIndex = e->setVariantProperty(*r, data); + + if (roleIndex != -1 && e->m_objectCache) { + QList<int> roles; + roles << roleIndex; + e->m_objectCache->updateValues(roles); + } + } + } + + return roleIndex; +} + +int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + const ListLayout::Role *r = m_layout->getExistingRole(key); + if (r) + roleIndex = e->setJsProperty(*r, data, eng); + } + + return roleIndex; +} + +inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) +{ + ListElement *e = this; + int blockIndex = 0; + while (blockIndex < role.blockIndex) { + if (e->next == 0) { + e->next = new ListElement; + e->next->uid = uid; + } + e = e->next; + ++blockIndex; + } + + char *mem = &e->data[role.blockOffset]; + return mem; +} + +QString *ListElement::getStringProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QString *s = reinterpret_cast<QString *>(mem); + return s->data_ptr() ? s : 0; +} + +QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QQmlGuard<QObject> *o = reinterpret_cast<QQmlGuard<QObject> *>(mem); + return o->data(); +} + +QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) +{ + QVariantMap *map = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed<QVariantMap>(mem)) + map = reinterpret_cast<QVariantMap *>(mem); + + return map; +} + +QQmlGuard<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard<QObject>) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + + QQmlGuard<QObject> *o = 0; + + if (existingGuard) + o = reinterpret_cast<QQmlGuard<QObject> *>(mem); + + return o; +} + +ListModel *ListElement::getListProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + ListModel **value = reinterpret_cast<ListModel **>(mem); + return *value; +} + +QVariant ListElement::getProperty(const ListLayout::Role &role, const QQuickListModel *owner, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + + QVariant data; + + switch (role.type) { + case ListLayout::Role::Number: + { + double *value = reinterpret_cast<double *>(mem); + data = *value; + } + break; + case ListLayout::Role::String: + { + QString *value = reinterpret_cast<QString *>(mem); + if (value->data_ptr() != 0) + data = *value; + } + break; + case ListLayout::Role::Bool: + { + bool *value = reinterpret_cast<bool *>(mem); + data = *value; + } + break; + case ListLayout::Role::List: + { + ListModel **value = reinterpret_cast<ListModel **>(mem); + ListModel *model = *value; + + if (model) { + if (model->m_modelCache == 0) { + model->m_modelCache = new QQuickListModel(owner, model, eng); + QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); + } + + QObject *object = model->m_modelCache; + data = QVariant::fromValue(object); + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard<QObject> *guard = reinterpret_cast<QQmlGuard<QObject> *>(mem); + QObject *object = guard->data(); + if (object) + data = QVariant::fromValue(object); + } + break; + case ListLayout::Role::VariantMap: + { + if (isMemoryUsed<QVariantMap>(mem)) { + QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); + data = *map; + } + } + break; + default: + break; + } + + return data; +} + +int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + QString *c = reinterpret_cast<QString *>(mem); + bool changed; + if (c->data_ptr() == 0) { + new (mem) QString(s); + changed = true; + } else { + changed = c->compare(s) != 0; + *c = s; + } + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Number) { + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + bool changed = *value != d; + *value = d; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Bool) { + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + bool changed = *value != b; + *value = b; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::List) { + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + if (*value) { + (*value)->destroy(); + delete *value; + } + *value = m; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::QObject) { + char *mem = getPropertyMemory(role); + QQmlGuard<QObject> *g = reinterpret_cast<QQmlGuard<QObject> *>(mem); + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard<QObject>) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + bool changed; + if (existingGuard) { + changed = g->data() != o; + g->~QQmlGuard(); + } else { + changed = true; + } + new (mem) QQmlGuard<QObject>(o); + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed<QVariantMap>(mem)) { + QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); + map->~QMap(); + } + new (mem) QVariantMap(eng->variantMapFromJS(o)); + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed<QVariantMap>(mem)) { + QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); + map->~QMap(); + } + if (m) + new (mem) QVariantMap(*m); + else + new (mem) QVariantMap; + roleIndex = role.index; + } + + return roleIndex; +} + +void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) +{ + char *mem = getPropertyMemory(role); + new (mem) QString(s); +} + +void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) +{ + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + *value = d; +} + +void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) +{ + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + *value = b; +} + +void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) +{ + char *mem = getPropertyMemory(role); + new (mem) QQmlGuard<QObject>(o); +} + +void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) +{ + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + *value = m; +} + +void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + QVariantMap *map = new (mem) QVariantMap; + *map = eng->variantMapFromJS(o); +} + +void ListElement::clearProperty(const ListLayout::Role &role) +{ + switch (role.type) { + case ListLayout::Role::String: + setStringProperty(role, QString()); + break; + case ListLayout::Role::Number: + setDoubleProperty(role, 0.0); + break; + case ListLayout::Role::Bool: + setBoolProperty(role, false); + break; + case ListLayout::Role::List: + setListProperty(role, 0); + break; + case ListLayout::Role::QObject: + setQObjectProperty(role, 0); + break; + case ListLayout::Role::VariantMap: + setVariantMapProperty(role, 0); + break; + default: + break; + } +} + +ListElement::ListElement() +{ + m_objectCache = 0; + uid = uidCounter.fetchAndAddOrdered(1); + next = 0; + qMemSet(data, 0, sizeof(data)); +} + +ListElement::ListElement(int existingUid) +{ + m_objectCache = 0; + uid = existingUid; + next = 0; + qMemSet(data, 0, sizeof(data)); +} + +ListElement::~ListElement() +{ + delete next; +} + +void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash) +{ + for (int i=0 ; i < srcLayout->roleCount() ; ++i) { + const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); + const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); + + switch (srcRole.type) { + case ListLayout::Role::List: + { + ListModel *srcSubModel = src->getListProperty(srcRole); + ListModel *targetSubModel = target->getListProperty(targetRole); + + if (srcSubModel) { + if (targetSubModel == 0) { + targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); + target->setListPropertyFast(targetRole, targetSubModel); + } + ListModel::sync(srcSubModel, targetSubModel, targetModelHash); + } + } + break; + case ListLayout::Role::QObject: + { + QObject *object = src->getQObjectProperty(srcRole); + target->setQObjectProperty(targetRole, object); + } + break; + case ListLayout::Role::String: + case ListLayout::Role::Number: + case ListLayout::Role::Bool: + { + QVariant v = src->getProperty(srcRole, 0, 0); + target->setVariantProperty(targetRole, v); + } + case ListLayout::Role::VariantMap: + { + QVariantMap *map = src->getVariantMapProperty(srcRole); + target->setVariantMapProperty(targetRole, map); + } + break; + default: + break; + } + } + +} + +void ListElement::destroy(ListLayout *layout) +{ + if (layout) { + for (int i=0 ; i < layout->roleCount() ; ++i) { + const ListLayout::Role &r = layout->getExistingRole(i); + + switch (r.type) { + case ListLayout::Role::String: + { + QString *string = getStringProperty(r); + if (string) + string->~QString(); + } + break; + case ListLayout::Role::List: + { + ListModel *model = getListProperty(r); + if (model) { + model->destroy(); + delete model; + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard<QObject> *guard = getGuardProperty(r); + if (guard) + guard->~QQmlGuard(); + } + break; + case ListLayout::Role::VariantMap: + { + QVariantMap *map = getVariantMapProperty(r); + if (map) + map->~QMap(); + } + break; + default: + // other types don't need explicit cleanup. + break; + } + } + + delete m_objectCache; + } + + if (next) + next->destroy(0); + uid = -1; +} + +int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) +{ + int roleIndex = -1; + + switch (role.type) { + case ListLayout::Role::Number: + roleIndex = setDoubleProperty(role, d.toDouble()); + break; + case ListLayout::Role::String: + roleIndex = setStringProperty(role, d.toString()); + break; + case ListLayout::Role::Bool: + roleIndex = setBoolProperty(role, d.toBool()); + break; + case ListLayout::Role::List: + roleIndex = setListProperty(role, d.value<ListModel *>()); + break; + case ListLayout::Role::VariantMap: { + QVariantMap map = d.toMap(); + roleIndex = setVariantMapProperty(role, &map); + } + break; + default: + break; + } + + return roleIndex; +} + +int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng) +{ + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (d->IsString()) { + v8::Handle<v8::String> jsString = d->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + roleIndex = setStringProperty(role, qstr); + } else if (d->IsNumber()) { + roleIndex = setDoubleProperty(role, d->NumberValue()); + } else if (d->IsArray()) { + ListModel *subModel = new ListModel(role.subLayout, 0, -1); + v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(d); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + roleIndex = setListProperty(role, subModel); + } else if (d->IsBoolean()) { + roleIndex = setBoolProperty(role, d->BooleanValue()); + } else if (d->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource(); + if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + roleIndex = setQObjectProperty(role, o); + } else if (role.type == ListLayout::Role::VariantMap) { + roleIndex = setVariantMapProperty(role, d->ToObject(), eng); + } + } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { + clearProperty(role); + } + + return roleIndex; +} + +ModelObject::ModelObject(QQuickListModel *model, int elementIndex) +: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) +{ + updateValues(); + setNodeUpdatesEnabled(true); +} + +void ModelObject::updateValues() +{ + int roleCount = m_model->m_listModel->roleCount(); + for (int i=0 ; i < roleCount ; ++i) { + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, i); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +void ModelObject::updateValues(const QList<int> &roles) +{ + int roleCount = roles.count(); + for (int i=0 ; i < roleCount ; ++i) { + int roleIndex = roles.at(i); + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, roleIndex); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) +: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) +{ +} + +ModelNodeMetaObject::~ModelNodeMetaObject() +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QV8Engine *eng = m_obj->m_model->engine(); + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(eng->context()); + + v8::Handle<v8::Value> v = eng->fromVariant(value); + + int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); + if (roleIndex != -1) { + QList<int> roles; + roles << roleIndex; + m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); + } +} + +DynamicRoleModelNode::DynamicRoleModelNode(QQuickListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) +{ + setNodeUpdatesEnabled(true); +} + +DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQuickListModel *owner) +{ + DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); + QList<int> roles; + object->updateValues(obj, roles); + return object; +} + +void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQuickListModel *> *targetModelHash) +{ + for (int i=0 ; i < src->m_meta->count() ; ++i) { + const QByteArray &name = src->m_meta->name(i); + QVariant value = src->m_meta->value(i); + + QQuickListModel *srcModel = qobject_cast<QQuickListModel *>(value.value<QObject *>()); + QQuickListModel *targetModel = qobject_cast<QQuickListModel *>(target->m_meta->value(i).value<QObject *>()); + + if (srcModel) { + if (targetModel == 0) + targetModel = QQuickListModel::createWithOwner(target->m_owner); + + QQuickListModel::sync(srcModel, targetModel, targetModelHash); + + QObject *targetModelObject = targetModel; + value = QVariant::fromValue(targetModelObject); + } else if (targetModel) { + delete targetModel; + } + + target->setValue(name, value); + } +} + +void DynamicRoleModelNode::updateValues(const QVariantMap &object, QList<int> &roles) +{ + const QList<QString> &keys = object.keys(); + + QList<QString>::const_iterator it = keys.begin(); + QList<QString>::const_iterator end = keys.end(); + + while (it != end) { + const QString &key = *it; + + int roleIndex = m_owner->m_roles.indexOf(key); + if (roleIndex == -1) { + roleIndex = m_owner->m_roles.count(); + m_owner->m_roles.append(key); + } + + QVariant value = object[key]; + + if (value.type() == QVariant::List) { + QQuickListModel *subModel = QQuickListModel::createWithOwner(m_owner); + + QVariantList subArray = value.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + value = QVariant::fromValue(subModelObject); + } + + const QByteArray &keyUtf8 = key.toUtf8(); + + QQuickListModel *existingModel = qobject_cast<QQuickListModel *>(m_meta->value(keyUtf8).value<QObject *>()); + if (existingModel) + delete existingModel; + + if (m_meta->setValue(keyUtf8, value)) + roles << roleIndex; + + ++it; + } +} + +DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) + : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) +{ +} + +DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() +{ + for (int i=0 ; i < count() ; ++i) { + QQuickListModel *subModel = qobject_cast<QQuickListModel *>(value(i).value<QObject *>()); + if (subModel) + delete subModel; + } +} + +void DynamicRoleModelNodeMetaObject::propertyWrite(int index) +{ + if (!m_enabled) + return; + + QVariant v = value(index); + QQuickListModel *model = qobject_cast<QQuickListModel *>(v.value<QObject *>()); + if (model) + delete model; +} + +void DynamicRoleModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QQuickListModel *parentModel = m_owner->m_owner; + + QVariant v = value(index); + if (v.type() == QVariant::List) { + QQuickListModel *subModel = QQuickListModel::createWithOwner(parentModel); + + QVariantList subArray = v.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + v = QVariant::fromValue(subModelObject); + + setValue(index, v); + } + + int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); + int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); + + if (elementIndex != -1 && roleIndex != -1) { + QList<int> roles; + roles << roleIndex; + + parentModel->emitItemsChanged(elementIndex, 1, roles); + } +} + +QQuickListModelParser::ListInstruction *QQuickListModelParser::ListModelData::instructions() const +{ + return (QQuickListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmlclass ListModel QQuickListModel + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet doc/src/snippets/qml/listmodel.qml 0 + + \clearfloat + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet doc/src/snippets/qml/listmodel-simple.qml 0 + \dots 8 + \snippet doc/src/snippets/qml/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet doc/src/snippets/qml/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet doc/src/snippets/qml/listmodel-nested.qml delegate + + \clearfloat + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet doc/src/snippets/qml/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml +*/ + +QQuickListModel::QQuickListModel(QObject *parent) +: QListModelInterface(parent) +{ + m_mainThread = true; + m_primary = true; + m_agent = 0; + m_uid = uidCounter.fetchAndAddOrdered(1); + m_dynamicRoles = false; + + m_layout = new ListLayout; + m_listModel = new ListModel(m_layout, this, -1); + + m_engine = 0; +} + +QQuickListModel::QQuickListModel(const QQuickListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) +: QListModelInterface(parent) +{ + m_mainThread = owner->m_mainThread; + m_primary = false; + m_agent = owner->m_agent; + + Q_ASSERT(owner->m_dynamicRoles == false); + m_dynamicRoles = false; + m_layout = 0; + m_listModel = data; + + m_engine = eng; +} + +QQuickListModel::QQuickListModel(QQuickListModel *orig, QQuickListModelWorkerAgent *agent) +: QListModelInterface(agent) +{ + m_mainThread = false; + m_primary = true; + m_agent = agent; + m_dynamicRoles = orig->m_dynamicRoles; + + m_layout = new ListLayout(orig->m_layout); + m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); + + if (m_dynamicRoles) + sync(orig, this, 0); + else + ListModel::sync(orig->m_listModel, m_listModel, 0); + + m_engine = 0; +} + +QQuickListModel::~QQuickListModel() +{ + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + + if (m_primary) { + m_listModel->destroy(); + delete m_listModel; + + if (m_mainThread && m_agent) { + m_agent->modelDestroyed(); + m_agent->release(); + } + } + + m_listModel = 0; + + delete m_layout; + m_layout = 0; +} + +QQuickListModel *QQuickListModel::createWithOwner(QQuickListModel *newOwner) +{ + QQuickListModel *model = new QQuickListModel; + + model->m_mainThread = newOwner->m_mainThread; + model->m_engine = newOwner->m_engine; + model->m_agent = newOwner->m_agent; + model->m_dynamicRoles = newOwner->m_dynamicRoles; + + if (model->m_mainThread && model->m_agent) + model->m_agent->addref(); + + QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); + + return model; +} + +QV8Engine *QQuickListModel::engine() const +{ + if (m_engine == 0) { + m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); + } + + return m_engine; +} + +void QQuickListModel::sync(QQuickListModel *src, QQuickListModel *target, QHash<int, QQuickListModel *> *targetModelHash) +{ + Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); + + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + target->m_roles = src->m_roles; + + // Build hash of elements <-> uid for each of the lists + QHash<int, ElementSync> elementHash; + for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = target->m_modelObjects.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = src->m_modelObjects.at(i); + int uid = e->getUid(); + + QHash<int, ElementSync>::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash<int, ElementSync>::iterator it = elementHash.begin(); + QHash<int, ElementSync>::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + int targetIndex = target->m_modelObjects.indexOf(s.target); + target->m_modelObjects.remove(targetIndex, 1); + delete s.target; + } + ++it; + } + + // Clear the target list, and append in correct order from the source + target->m_modelObjects.clear(); + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + DynamicRoleModelNode *targetElement = s.target; + if (targetElement == 0) { + targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); + } + DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); + target->m_modelObjects.append(targetElement); + } +} + +void QQuickListModel::emitItemsChanged(int index, int count, const QList<int> &roles) +{ + if (m_mainThread) { + emit itemsChanged(index, count, roles); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.changedChange(uid, index, count, roles); + } +} + +void QQuickListModel::emitItemsRemoved(int index, int count) +{ + if (m_mainThread) { + emit itemsRemoved(index, count); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + if (index == 0 && count == this->count()) + m_agent->data.clearChange(uid); + m_agent->data.removeChange(uid, index, count); + } +} + +void QQuickListModel::emitItemsInserted(int index, int count) +{ + if (m_mainThread) { + emit itemsInserted(index, count); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.insertChange(uid, index, count); + } +} + +void QQuickListModel::emitItemsMoved(int from, int to, int n) +{ + if (m_mainThread) { + emit itemsMoved(from, to, n); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.moveChange(uid, from, n, to); + } +} + +QQuickListModelWorkerAgent *QQuickListModel::agent() +{ + if (m_agent) + return m_agent; + + m_agent = new QQuickListModelWorkerAgent(this); + return m_agent; +} + +QList<int> QQuickListModel::roles() const +{ + QList<int> rolesArray; + + if (m_dynamicRoles) { + for (int i=0 ; i < m_roles.count() ; ++i) + rolesArray << i; + } else { + for (int i=0 ; i < m_listModel->roleCount() ; ++i) + rolesArray << i; + } + + return rolesArray; +} + +QString QQuickListModel::toString(int role) const +{ + QString roleName; + + if (m_dynamicRoles) { + roleName = m_roles[role]; + } else { + const ListLayout::Role &r = m_listModel->getExistingRole(role); + roleName = r.name; + } + + return roleName; +} + +QVariant QQuickListModel::data(int index, int role) const +{ + QVariant v; + + if (index >= count() || index < 0) + return v; + + if (m_dynamicRoles) + v = m_modelObjects[index]->getValue(m_roles[role]); + else + v = m_listModel->getProperty(index, role, this, engine()); + + return v; +} + +/*! + \qmlproperty bool QtQuick2::ListModel::dynamicRoles + + By default, the type of a role is fixed the first time + the role is used. For example, if you create a role called + "data" and assign a number to it, you can no longer assign + a string to the "data" role. However, when the dynamicRoles + property is enabled, the type of a given role is not fixed + and can be different between elements. + + The dynamicRoles property must be set before any data is + added to the ListModel, and must be set from the main + thread. + + A ListModel that has data statically defined (via the + ListElement QML syntax) cannot have the dynamicRoles + property enabled. + + There is a significant performance cost to using a + ListModel with dynamic roles enabled. The cost varies + from platform to platform but is typically somewhere + between 4-6x slower than using static role types. + + Due to the performance cost of using dynamic roles, + they are disabled by default. +*/ +void QQuickListModel::setDynamicRoles(bool enableDynamicRoles) +{ + if (m_mainThread && m_agent == 0) { + if (enableDynamicRoles) { + if (m_layout->roleCount()) + qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); + else + m_dynamicRoles = true; + } else { + if (m_roles.count()) { + qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); + } else { + m_dynamicRoles = false; + } + } + } else { + qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); + } +} + +/*! + \qmlproperty int QtQuick2::ListModel::count + The number of data entries in the model. +*/ +int QQuickListModel::count() const +{ + int count; + + if (m_dynamicRoles) + count = m_modelObjects.count(); + else { + count = m_listModel->elementCount(); + } + + return count; +} + +/*! + \qmlmethod QtQuick2::ListModel::clear() + + Deletes all content from the model. + + \sa append() remove() +*/ +void QQuickListModel::clear() +{ + int cleared = count(); + + if (m_dynamicRoles) { + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + m_modelObjects.clear(); + } else { + m_listModel->clear(); + } + + emitItemsRemoved(0, cleared); +} + +/*! + \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QQuickListModel::remove(QQmlV8Function *args) +{ + int argLength = args->Length(); + + if (argLength == 1 || argLength == 2) { + int index = (*args)[0]->Int32Value(); + int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); + + if (index < 0 || index+removeCount > count() || removeCount <= 0) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); + return; + } + + if (m_dynamicRoles) { + for (int i=0 ; i < removeCount ; ++i) + delete m_modelObjects[index+i]; + m_modelObjects.remove(index, removeCount); + } else { + m_listModel->remove(index, removeCount); + } + + emitItemsRemoved(index, removeCount); + } else { + qmlInfo(this) << tr("remove: incorrect number of arguments"); + } +} + +/*! + \qmlmethod QtQuick2::ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set() append() +*/ + +void QQuickListModel::insert(QQmlV8Function *args) +{ + if (args->Length() == 2) { + + v8::Handle<v8::Value> arg0 = (*args)[0]; + int index = arg0->Int32Value(); + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + v8::Handle<v8::Value> arg1 = (*args)[1]; + + if (arg1->IsArray()) { + v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1); + int objectArrayLength = objectArray->Length(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index+i, argObject, args->engine()); + } + } + emitItemsInserted(index, objectArrayLength); + } else if (arg1->IsObject()) { + v8::Handle<v8::Object> argObject = arg1->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index, argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } +} + +/*! + \qmlmethod QtQuick2::ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + fruitModel.move(0, fruitModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQuickListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + if (m_dynamicRoles) { + + int realFrom = from; + int realTo = to; + int realN = n; + + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + realFrom = tto; + realTo = tto+n; + realN = tfrom-tto; + } + + QPODVector<DynamicRoleModelNode *, 4> store; + for (int i=0 ; i < (realTo-realFrom) ; ++i) + store.append(m_modelObjects[realFrom+realN+i]); + for (int i=0 ; i < realN ; ++i) + store.append(m_modelObjects[realFrom+i]); + for (int i=0 ; i < store.count() ; ++i) + m_modelObjects[realFrom+i] = store[i]; + + } else { + m_listModel->move(from, to, n); + } + + emitItemsMoved(from, to, n); +} + +/*! + \qmlmethod QtQuick2::ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + fruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QQuickListModel::append(QQmlV8Function *args) +{ + if (args->Length() == 1) { + v8::Handle<v8::Value> arg = (*args)[0]; + + if (arg->IsArray()) { + v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg); + int objectArrayLength = objectArray->Length(); + + int index = count(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->append(argObject, args->engine()); + } + } + + emitItemsInserted(index, objectArrayLength); + } else if (arg->IsObject()) { + v8::Handle<v8::Object> argObject = arg->ToObject(); + + int index; + + if (m_dynamicRoles) { + index = m_modelObjects.count(); + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + index = m_listModel->append(argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("append: value is not an object"); + } + } else { + qmlInfo(this) << tr("append: value is not an object"); + } +} + +/*! + \qmlmethod object QtQuick2::ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QQmlV8Handle QQuickListModel::get(int index) const +{ + v8::Handle<v8::Value> result = v8::Undefined(); + + if (index >= 0 && index < count()) { + QV8Engine *v8engine = engine(); + + if (m_dynamicRoles) { + DynamicRoleModelNode *object = m_modelObjects[index]; + result = v8engine->newQObject(object); + } else { + ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QQuickListModel *>(this), index); + result = v8engine->newQObject(object); + } + } + + return QQmlV8Handle::fromHandle(result); +} + +/*! + \qmlmethod QtQuick2::ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QQuickListModel::set(int index, const QQmlV8Handle &handle) +{ + v8::Handle<v8::Value> valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + v8::Handle<v8::Object> object = valuemap->ToObject(); + + if (index == count()) { + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); + } else { + m_listModel->insert(index, object, engine()); + } + + emitItemsInserted(index, 1); + } else { + + QList<int> roles; + + if (m_dynamicRoles) { + m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); + } else { + m_listModel->set(index, object, &roles, engine()); + } + + if (roles.count()) + emitItemsChanged(index, 1, roles); + } +} + +/*! + \qmlmethod QtQuick2::ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QQuickListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_dynamicRoles) { + int roleIndex = m_roles.indexOf(property); + if (roleIndex == -1) { + roleIndex = m_roles.count(); + m_roles.append(property); + } + if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { + QList<int> roles; + roles << roleIndex; + emitItemsChanged(index, 1, roles); + } + } else { + int roleIndex = m_listModel->setOrCreateProperty(index, property, value); + if (roleIndex != -1) { + + QList<int> roles; + roles << roleIndex; + + emitItemsChanged(index, 1, roles); + } + } +} + +/*! + \qmlmethod QtQuick2::ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QQuickListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QQuickListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QQuickListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data) +{ + QList<QVariant> values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId<QQmlCustomParserNode>()) { + QQmlCustomParserNode node = + qvariant_cast<QQmlCustomParserNode>(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QQuickListElement::staticMetaObject) { + error(node, QQuickListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList<QQmlCustomParserProperty> props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QQmlCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QQuickListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == QStringLiteral("id")) { + error(nodeProp, QQuickListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name().toUtf8()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QQmlScript::Variant variant = + qvariant_cast<QQmlScript::Variant>(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QQmlScript::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + int v = evaluateEnum(script); + if (v<0) { + using namespace QQmlJS; + AST::Node *node = variant.asAST(); + AST::StringLiteral *literal = 0; + if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) { + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) { + if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { + if (callExpr->arguments && !callExpr->arguments->next) + literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression); + if (!literal) { + error(prop, QQuickListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); + return false; + } + } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { + if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) + literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression); + if (!literal) { + error(prop, QQuickListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); + return false; + } + } + } + } + + if (literal) { + d[0] = char(QQmlScript::Variant::String); + d += literal->value.toUtf8(); + } else { + error(prop, QQuickListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QQmlScript::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QQuickListModelParser::compile(const QList<QQmlCustomParserProperty> &customProps) +{ + QList<ListInstruction> instr; + QByteArray data; + listElementTypeName = QString(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QQmlCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QQuickListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QQuickListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QQuickListModel *rv = static_cast<QQuickListModel *>(obj); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); + rv->m_engine = engine; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + QStack<DataStackElement> stack; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + Q_ASSERT(!rv->m_dynamicRoles); + + ListModel *subModel = 0; + + if (stack.count() == 0) { + subModel = rv->m_listModel; + } else { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + if (role.type == ListLayout::Role::List) { + subModel = e1.model->getListProperty(e1.elementIndex, role); + + if (subModel == 0) { + subModel = new ListModel(role.subLayout, 0, -1); + QVariant vModel = QVariant::fromValue(subModel); + e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel); + } + } + } + + DataStackElement e; + e.model = subModel; + e.elementIndex = subModel ? subModel->appendElement() : -1; + stack.push(e); + } + break; + + case ListInstruction::Pop: + stack.pop(); + break; + + case ListInstruction::Value: + { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + QString name = e0.name; + QVariant value; + + switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { + case QQmlScript::Variant::Invalid: + { + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); + value = QVariant::fromValue(emptyModel); + } + break; + case QQmlScript::Variant::Boolean: + value = bool(data[1 + instr.dataIdx]); + break; + case QQmlScript::Variant::Number: + value = QByteArray(data + 1 + instr.dataIdx).toDouble(); + break; + case QQmlScript::Variant::String: + value = QString::fromUtf8(data + 1 + instr.dataIdx); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + e1.model->setOrCreateProperty(e1.elementIndex, name, value); + } + break; + + case ListInstruction::Set: + { + DataStackElement e; + e.name = QString::fromUtf8(data + instr.dataIdx); + stack.push(e); + } + break; + } + } +} + +bool QQuickListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i<s.length()-1; i++) { + if (!s[i].isSpace()) + return false; + } + return true; + } + return false; +} + + +/*! + \qmlclass ListElement QQuickListElement + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The ListElement element defines a data item in a ListModel. + + List elements are defined inside ListModel definitions, and represent items in a + list that will be displayed using ListView or \l Repeater items. + + List elements are defined like other QML elements except that they contain + a collection of \e role definitions instead of properties. Using the same + syntax as property definitions, roles both define how the data is accessed + and include the data itself. + + The names used for roles must begin with a lower-case letter and should be + common to all elements in a given model. Values must be simple constants; either + strings (quoted and optionally within a call to QT_TR_NOOP), boolean values + (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). + + \section1 Referencing Roles + + The role names are used by delegates to obtain data from list elements. + Each role name is accessible in the delegate's scope, and refers to the + corresponding role in the current element. Where a role name would be + ambiguous to use, it can be accessed via the \l{ListView::}{model} + property (e.g., \c{model.cost} instead of \c{cost}). + + \section1 Example Usage + + The following model defines a series of list elements, each of which + contain "name" and "cost" roles and their associated values. + + \snippet doc/src/snippets/qml/qml-data-models/listelements.qml model + + The delegate obtains the name and cost for each element by simply referring + to \c name and \c cost: + + \snippet doc/src/snippets/qml/qml-data-models/listelements.qml view + + \sa ListModel +*/ + +QT_END_NAMESPACE diff --git a/src/qml/qml/qquicklistmodel_p.h b/src/qml/qml/qquicklistmodel_p.h new file mode 100644 index 0000000000..2941de9148 --- /dev/null +++ b/src/qml/qml/qquicklistmodel_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODEL_H +#define QQUICKLISTMODEL_H + +#include <qqml.h> +#include <private/qqmlcustomparser_p.h> + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QVariant> +#include "qlistmodelinterface_p.h" + +#include <private/qv8engine_p.h> +#include <private/qpodvector_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQuickListModelWorkerAgent; +class ListModel; +class ListLayout; + +class Q_QML_PRIVATE_EXPORT QQuickListModel : public QListModelInterface +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) + +public: + QQuickListModel(QObject *parent=0); + ~QQuickListModel(); + + virtual QList<int> roles() const; + virtual QString toString(int role) const; + virtual int count() const; + virtual QVariant data(int index, int role) const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QQuickListModelWorkerAgent *agent(); + + bool dynamicRoles() const { return m_dynamicRoles; } + void setDynamicRoles(bool enableDynamicRoles); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QQuickListModelParser; + friend class QQuickListModelWorkerAgent; + friend class ModelObject; + friend class ModelNodeMetaObject; + friend class ListModel; + friend class ListElement; + friend class DynamicRoleModelNode; + friend class DynamicRoleModelNodeMetaObject; + + // Constructs a flat list model for a worker agent + QQuickListModel(QQuickListModel *orig, QQuickListModelWorkerAgent *agent); + QQuickListModel(const QQuickListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); + + QV8Engine *engine() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QQuickListModelWorkerAgent *m_agent; + mutable QV8Engine *m_engine; + bool m_mainThread; + bool m_primary; + + bool m_dynamicRoles; + + ListLayout *m_layout; + ListModel *m_listModel; + + QVector<class DynamicRoleModelNode *> m_modelObjects; + QVector<QString> m_roles; + int m_uid; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + DynamicRoleModelNode *src; + DynamicRoleModelNode *target; + }; + + int getUid() const { return m_uid; } + + static void sync(QQuickListModel *src, QQuickListModel *target, QHash<int, QQuickListModel *> *targetModelHash); + static QQuickListModel *createWithOwner(QQuickListModel *newOwner); + + void emitItemsChanged(int index, int count, const QList<int> &roles); + void emitItemsRemoved(int index, int count); + void emitItemsInserted(int index, int count); + void emitItemsMoved(int from, int to, int n); +}; + +// ### FIXME +class QQuickListElement : public QObject +{ +Q_OBJECT +}; + +class QQuickListModelParser : public QQmlCustomParser +{ +public: + QQuickListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} + QByteArray compile(const QList<QQmlCustomParserProperty> &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QString listElementTypeName; + + struct DataStackElement + { + DataStackElement() : model(0), elementIndex(0) {} + + QString name; + ListModel *model; + int elementIndex; + }; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickListModel) +QML_DECLARE_TYPE(QQuickListElement) + +QT_END_HEADER + +#endif // QQUICKLISTMODEL_H diff --git a/src/qml/qml/qquicklistmodel_p_p.h b/src/qml/qml/qquicklistmodel_p_p.h new file mode 100644 index 0000000000..f9256c9f0a --- /dev/null +++ b/src/qml/qml/qquicklistmodel_p_p.h @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODEL_P_P_H +#define QQUICKLISTMODEL_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 "qquicklistmodel_p.h" +#include <private/qqmlengine_p.h> +#include "qqmlopenmetaobject_p.h" +#include <qqml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class DynamicRoleModelNode; + +class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); + ~DynamicRoleModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWrite(int index); + void propertyWritten(int index); + +private: + DynamicRoleModelNode *m_owner; +}; + +class DynamicRoleModelNode : public QObject +{ + Q_OBJECT +public: + DynamicRoleModelNode(QQuickListModel *owner, int uid); + + static DynamicRoleModelNode *create(const QVariantMap &obj, QQuickListModel *owner); + + void updateValues(const QVariantMap &object, QList<int> &roles); + + QVariant getValue(const QString &name) + { + return m_meta->value(name.toUtf8()); + } + + bool setValue(const QByteArray &name, const QVariant &val) + { + return m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + int getUid() const + { + return m_uid; + } + + static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQuickListModel *> *targetModelHash); + +private: + QQuickListModel *m_owner; + int m_uid; + DynamicRoleModelNodeMetaObject *m_meta; + + friend class DynamicRoleModelNodeMetaObject; +}; + +class ModelObject; + +class ModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + ModelNodeMetaObject(ModelObject *object); + ~ModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + + ModelObject *m_obj; +}; + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(QQuickListModel *model, int elementIndex); + + void setValue(const QByteArray &name, const QVariant &val, bool force) + { + if (force) { + QVariant existingValue = m_meta->value(name); + if (existingValue.isValid()) { + (*m_meta)[name] = QVariant(); + } + } + m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + void updateValues(); + void updateValues(const QList<int> &roles); + + QQuickListModel *m_model; + int m_elementIndex; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ListLayout +{ +public: + ListLayout() : currentBlock(0), currentBlockOffset(0) {} + ListLayout(const ListLayout *other); + ~ListLayout(); + + class Role + { + public: + + Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} + explicit Role(const Role *other); + ~Role(); + + // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp + enum DataType + { + Invalid = -1, + + String, + Number, + Bool, + List, + QObject, + VariantMap, + + MaxDataType + }; + + QString name; + DataType type; + int blockIndex; + int blockOffset; + int index; + ListLayout *subLayout; + }; + + const Role *getRoleOrCreate(const QString &key, const QVariant &data); + const Role &getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type); + const Role &getRoleOrCreate(const QString &key, Role::DataType type); + + const Role &getExistingRole(int index) { return *roles.at(index); } + const Role *getExistingRole(const QString &key); + const Role *getExistingRole(v8::Handle<v8::String> key); + + int roleCount() const { return roles.count(); } + + static void sync(ListLayout *src, ListLayout *target); + +private: + const Role &createRole(const QString &key, Role::DataType type); + + int currentBlock; + int currentBlockOffset; + QVector<Role *> roles; + QStringHash<Role *> roleHash; +}; + +class ListElement +{ +public: + + ListElement(); + ListElement(int existingUid); + ~ListElement(); + + static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash); + + enum + { + BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) + }; + +private: + + void destroy(ListLayout *layout); + + int setVariantProperty(const ListLayout::Role &role, const QVariant &d); + + int setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng); + + int setStringProperty(const ListLayout::Role &role, const QString &s); + int setDoubleProperty(const ListLayout::Role &role, double n); + int setBoolProperty(const ListLayout::Role &role, bool b); + int setListProperty(const ListLayout::Role &role, ListModel *m); + int setQObjectProperty(const ListLayout::Role &role, QObject *o); + int setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng); + int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); + + void setStringPropertyFast(const ListLayout::Role &role, const QString &s); + void setDoublePropertyFast(const ListLayout::Role &role, double n); + void setBoolPropertyFast(const ListLayout::Role &role, bool b); + void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); + void setListPropertyFast(const ListLayout::Role &role, ListModel *m); + void setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng); + + void clearProperty(const ListLayout::Role &role); + + QVariant getProperty(const ListLayout::Role &role, const QQuickListModel *owner, QV8Engine *eng); + ListModel *getListProperty(const ListLayout::Role &role); + QString *getStringProperty(const ListLayout::Role &role); + QObject *getQObjectProperty(const ListLayout::Role &role); + QQmlGuard<QObject> *getGuardProperty(const ListLayout::Role &role); + QVariantMap *getVariantMapProperty(const ListLayout::Role &role); + + inline char *getPropertyMemory(const ListLayout::Role &role); + + int getUid() const { return uid; } + + char data[BLOCK_SIZE]; + ListElement *next; + + int uid; + ModelObject *m_objectCache; + + friend class ListModel; +}; + +class ListModel +{ +public: + + ListModel(ListLayout *layout, QQuickListModel *modelCache, int uid); + ~ListModel() {} + + void destroy(); + + int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); + int setExistingProperty(int uid, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng); + + QVariant getProperty(int elementIndex, int roleIndex, const QQuickListModel *owner, QV8Engine *eng); + ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); + + int roleCount() const + { + return m_layout->roleCount(); + } + + const ListLayout::Role &getExistingRole(int index) + { + return m_layout->getExistingRole(index); + } + + const ListLayout::Role &getOrCreateListRole(const QString &name) + { + return m_layout->getRoleOrCreate(name, ListLayout::Role::List); + } + + int elementCount() const + { + return elements.count(); + } + + void set(int elementIndex, v8::Handle<v8::Object> object, QList<int> *roles, QV8Engine *eng); + void set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng); + + int append(v8::Handle<v8::Object> object, QV8Engine *eng); + void insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng); + + void clear(); + void remove(int index, int count); + + int appendElement(); + void insertElement(int index); + + void move(int from, int to, int n); + + int getUid() const { return m_uid; } + + static void sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *srcModelHash); + + ModelObject *getOrCreateModelObject(QQuickListModel *model, int elementIndex); + +private: + QPODVector<ListElement *, 4> elements; + ListLayout *m_layout; + int m_uid; + + QQuickListModel *m_modelCache; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + ListElement *src; + ListElement *target; + }; + + void newElement(int index); + + void updateCacheIndices(); + + friend class ListElement; + friend class QQuickListModelWorkerAgent; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ListModel *); + +QT_END_HEADER + +#endif // QQUICKLISTMODEL_P_P_H + diff --git a/src/qml/qml/qquicklistmodelworkeragent.cpp b/src/qml/qml/qquicklistmodelworkeragent.cpp new file mode 100644 index 0000000000..c50b348a4a --- /dev/null +++ b/src/qml/qml/qquicklistmodelworkeragent.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklistmodelworkeragent_p.h" +#include "qquicklistmodel_p_p.h" +#include <private/qqmldata_p.h> +#include <private/qqmlengine_p.h> +#include <qqmlinfo.h> + +#include <QtCore/qcoreevent.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + + +void QQuickListModelWorkerAgent::Data::clearChange(int uid) +{ + for (int i=0 ; i < changes.count() ; ++i) { + if (changes[i].modelUid == uid) { + changes.removeAt(i); + --i; + } + } +} + +void QQuickListModelWorkerAgent::Data::insertChange(int uid, int index, int count) +{ + Change c = { uid, Change::Inserted, index, count, 0, QList<int>() }; + changes << c; +} + +void QQuickListModelWorkerAgent::Data::removeChange(int uid, int index, int count) +{ + Change c = { uid, Change::Removed, index, count, 0, QList<int>() }; + changes << c; +} + +void QQuickListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) +{ + Change c = { uid, Change::Moved, index, count, to, QList<int>() }; + changes << c; +} + +void QQuickListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QList<int> &roles) +{ + Change c = { uid, Change::Changed, index, count, 0, roles }; + changes << c; +} + +QQuickListModelWorkerAgent::QQuickListModelWorkerAgent(QQuickListModel *model) +: m_ref(1), m_orig(model), m_copy(new QQuickListModel(model, this)) +{ +} + +QQuickListModelWorkerAgent::~QQuickListModelWorkerAgent() +{ + mutex.lock(); + syncDone.wakeAll(); + mutex.unlock(); +} + +void QQuickListModelWorkerAgent::setV8Engine(QV8Engine *eng) +{ + m_copy->m_engine = eng; +} + +void QQuickListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QQuickListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + deleteLater(); +} + +void QQuickListModelWorkerAgent::modelDestroyed() +{ + m_orig = 0; +} + +int QQuickListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QQuickListModelWorkerAgent::clear() +{ + m_copy->clear(); +} + +void QQuickListModelWorkerAgent::remove(QQmlV8Function *args) +{ + m_copy->remove(args); +} + +void QQuickListModelWorkerAgent::append(QQmlV8Function *args) +{ + m_copy->append(args); +} + +void QQuickListModelWorkerAgent::insert(QQmlV8Function *args) +{ + m_copy->insert(args); +} + +QQmlV8Handle QQuickListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QQuickListModelWorkerAgent::set(int index, const QQmlV8Handle &value) +{ + m_copy->set(index, value); +} + +void QQuickListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + m_copy->setProperty(index, property, value); +} + +void QQuickListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); +} + +void QQuickListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +bool QQuickListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + bool cc = false; + QMutexLocker locker(&mutex); + if (m_orig) { + Sync *s = static_cast<Sync *>(e); + const QList<Change> &changes = s->data.changes; + + cc = m_orig->count() != s->list->count(); + + QHash<int, QQuickListModel *> targetModelDynamicHash; + QHash<int, ListModel *> targetModelStaticHash; + + Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); + if (m_orig->m_dynamicRoles) + QQuickListModel::sync(s->list, m_orig, &targetModelDynamicHash); + else + ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + + QQuickListModel *model = 0; + if (m_orig->m_dynamicRoles) { + model = targetModelDynamicHash.value(change.modelUid); + } else { + ListModel *lm = targetModelStaticHash.value(change.modelUid); + if (lm) + model = lm->m_modelCache; + } + + if (model) { + switch (change.type) { + case Change::Inserted: + emit model->itemsInserted(change.index, change.count); + break; + case Change::Removed: + emit model->itemsRemoved(change.index, change.count); + break; + case Change::Moved: + emit model->itemsMoved(change.index, change.to, change.count); + break; + case Change::Changed: + emit model->itemsChanged(change.index, change.count, change.roles); + break; + } + } + } + } + + syncDone.wakeAll(); + locker.unlock(); + + if (cc) + emit m_orig->countChanged(); + return true; + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qquicklistmodelworkeragent_p.h b/src/qml/qml/qquicklistmodelworkeragent_p.h new file mode 100644 index 0000000000..cf2ef45c16 --- /dev/null +++ b/src/qml/qml/qquicklistmodelworkeragent_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODELWORKERAGENT_P_H +#define QQUICKLISTMODELWORKERAGENT_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 <qqml.h> + +#include <QtGui/qevent.h> +#include <QMutex> +#include <QWaitCondition> + +#include <private/qv8engine_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQuickListModel; + +class QQuickListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QQuickListModelWorkerAgent(QQuickListModel *); + ~QQuickListModelWorkerAgent(); + void setV8Engine(QV8Engine *eng); + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QQuickListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QQuickListModelWorkerAgent *a; + }; + void modelDestroyed(); +protected: + virtual bool event(QEvent *); + +private: + friend class QQuickWorkerScriptEnginePrivate; + friend class QQuickListModel; + + struct Change + { + int modelUid; + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QList<int> roles; + }; + + struct Data + { + QList<Change> changes; + + void clearChange(int uid); + void insertChange(int uid, int index, int count); + void removeChange(int uid, int index, int count); + void moveChange(int uid, int index, int count, int to); + void changedChange(int uid, int index, int count, const QList<int> &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QQuickListModel *list; + }; + + QAtomicInt m_ref; + QQuickListModel *m_orig; + QQuickListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQuickListModelWorkerAgent::VariantRef) + +QT_END_HEADER + +#endif // QQUICKLISTMODELWORKERAGENT_P_H + diff --git a/src/qml/qml/qquickworkerscript.cpp b/src/qml/qml/qquickworkerscript.cpp new file mode 100644 index 0000000000..f91aa28509 --- /dev/null +++ b/src/qml/qml/qquickworkerscript.cpp @@ -0,0 +1,730 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickworkerscript_p.h" +#include "qquicklistmodel_p.h" +#include "qquicklistmodelworkeragent_p.h" +#include "qqmlengine_p.h" +#include "qqmlexpression_p.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdebug.h> +#include <QtQml/qjsengine.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qfile.h> +#include <QtCore/qdatetime.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#include <QtQml/qqmlinfo.h> +#include "qqmlnetworkaccessmanagerfactory.h" + +#include <private/qv8engine_p.h> +#include <private/qv8worker_p.h> + +QT_BEGIN_NAMESPACE + +class WorkerDataEvent : public QEvent +{ +public: + enum Type { WorkerData = QEvent::User }; + + WorkerDataEvent(int workerId, const QByteArray &data); + virtual ~WorkerDataEvent(); + + int workerId() const; + QByteArray data() const; + +private: + int m_id; + QByteArray m_data; +}; + +class WorkerLoadEvent : public QEvent +{ +public: + enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; + + WorkerLoadEvent(int workerId, const QUrl &url); + + int workerId() const; + QUrl url() const; + +private: + int m_id; + QUrl m_url; +}; + +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + +class WorkerErrorEvent : public QEvent +{ +public: + enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; + + WorkerErrorEvent(const QQmlError &error); + + QQmlError error() const; + +private: + QQmlError m_error; +}; + +class QQuickWorkerScriptEnginePrivate : public QObject +{ + Q_OBJECT +public: + enum WorkerEventTypes { + WorkerDestroyEvent = QEvent::User + 100 + }; + + QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); + + class WorkerEngine : public QV8Engine + { + public: + WorkerEngine(QQuickWorkerScriptEnginePrivate *parent); + ~WorkerEngine(); + + void init(); + virtual QNetworkAccessManager *networkAccessManager(); + + QQuickWorkerScriptEnginePrivate *p; + + v8::Local<v8::Function> sendFunction(int id); + void callOnMessage(v8::Handle<v8::Object> object, v8::Handle<v8::Value> arg); + private: + v8::Persistent<v8::Function> onmessage; + v8::Persistent<v8::Function> createsend; + QNetworkAccessManager *accessManager; + }; + + WorkerEngine *workerEngine; + static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) { + return static_cast<WorkerEngine *>(e)->p; + } + + QQmlEngine *qmlengine; + + QMutex m_lock; + QWaitCondition m_wait; + + struct WorkerScript { + WorkerScript(); + ~WorkerScript(); + + int id; + QUrl source; + bool initialized; + QQuickWorkerScript *owner; + v8::Persistent<v8::Object> object; + }; + + QHash<int, WorkerScript *> workers; + v8::Handle<v8::Object> getWorker(WorkerScript *); + + int m_nextId; + + static v8::Handle<v8::Value> sendMessage(const v8::Arguments &args); + +signals: + void stopThread(); + +protected: + virtual bool event(QEvent *); + +private: + void processMessage(int, const QByteArray &); + void processLoad(int, const QUrl &); + void reportScriptException(WorkerScript *, const QQmlError &error); +}; + +QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) +: QV8Engine(0), p(parent), accessManager(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() +{ + qPersistentDispose(createsend); + qPersistentDispose(onmessage); + delete accessManager; +} + +void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() +{ + initDeclarativeGlobalObject(); +#define CALL_ONMESSAGE_SCRIPT \ + "(function(object, message) { "\ + "var isfunction = false; "\ + "try { "\ + "isfunction = object.WorkerScript.onMessage instanceof Function; "\ + "} catch (e) {}" \ + "if (isfunction) "\ + "object.WorkerScript.onMessage(message); "\ + "})" + +#define SEND_MESSAGE_CREATE_SCRIPT \ + "(function(method, engine) { "\ + "return (function(id) { "\ + "return (function(message) { "\ + "if (arguments.length) method(engine, id, message); "\ + "}); "\ + "}); "\ + "})" + + v8::HandleScope handle_scope; + v8::Context::Scope scope(context()); + + { + v8::Local<v8::Script> onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT)); + onmessage = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(onmessagescript->Run())); + } + { + v8::Local<v8::Script> createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT)); + v8::Local<v8::Function> createsendconstructor = v8::Local<v8::Function>::Cast(createsendscript->Run()); + + v8::Handle<v8::Value> args[] = { + V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this) + }; + v8::Local<v8::Value> createsendvalue = createsendconstructor->Call(global(), 1, args); + + createsend = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(createsendvalue)); + } +} + +// Requires handle and context scope +v8::Local<v8::Function> QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id) +{ + v8::Handle<v8::Value> args[] = { v8::Integer::New(id) }; + return v8::Local<v8::Function>::Cast(createsend->Call(global(), 1, args)); +} + +// Requires handle and context scope +void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle<v8::Object> object, + v8::Handle<v8::Value> arg) +{ + v8::Handle<v8::Value> args[] = { object, arg }; + onmessage->Call(global(), 2, args); +} + +QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() +{ + if (!accessManager) { + if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { + accessManager = p->qmlengine->networkAccessManagerFactory()->create(p); + } else { + accessManager = new QNetworkAccessManager(p); + } + } + return accessManager; +} + +QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) +: workerEngine(0), qmlengine(engine), m_nextId(0) +{ +} + +v8::Handle<v8::Value> QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args) +{ + WorkerEngine *engine = (WorkerEngine*)V8ENGINE(); + + int id = args[1]->Int32Value(); + + QByteArray data = QV8Worker::serialize(args[2], engine); + + QMutexLocker(&engine->p->m_lock); + WorkerScript *script = engine->p->workers.value(id); + if (!script) + return v8::Undefined(); + + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); + + return v8::Undefined(); +} + +// Requires handle scope and context scope +v8::Handle<v8::Object> QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) +{ + if (!script->initialized) { + script->initialized = true; + + script->object = qPersistentNew<v8::Object>(workerEngine->contextWrapper()->urlScope(script->source)); + + workerEngine->contextWrapper()->setReadOnly(script->object, false); + + v8::Local<v8::Object> api = v8::Object::New(); + api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id)); + + script->object->Set(v8::String::New("WorkerScript"), api); + + workerEngine->contextWrapper()->setReadOnly(script->object, true); + } + + return script->object; +} + +bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); + processMessage(workerEvent->workerId(), workerEvent->data()); + return true; + } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { + WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); + processLoad(workerEvent->workerId(), workerEvent->url()); + return true; + } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { + emit stopThread(); + return true; + } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { + WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event); + workers.remove(workerEvent->workerId()); + return true; + } else { + return QObject::event(event); + } +} + +void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) +{ + WorkerScript *script = workers.value(id); + if (!script) + return; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + v8::Handle<v8::Value> value = QV8Worker::deserialize(data, workerEngine); + + v8::TryCatch tc; + workerEngine->callOnMessage(script->object, value); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } +} + +void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) +{ + if (url.isRelative()) + return; + + QString fileName = QQmlEnginePrivate::urlToLocalFileOrQrc(url); + + QFile f(fileName); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString sourceCode = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(sourceCode); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + WorkerScript *script = workers.value(id); + if (!script) + return; + script->source = url; + v8::Handle<v8::Object> activation = getWorker(script); + if (activation.IsEmpty()) + return; + + // XXX ??? + // workerEngine->baseUrl = url; + + v8::TryCatch tc; + v8::Local<v8::Script> program = workerEngine->qmlModeCompile(sourceCode, url.toString()); + + if (!tc.HasCaught()) + program->Run(activation); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } + } else { + qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + } +} + +void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, + const QQmlError &error) +{ + QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine); + + QMutexLocker(&p->m_lock); + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) +: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) +{ +} + +WorkerDataEvent::~WorkerDataEvent() +{ +} + +int WorkerDataEvent::workerId() const +{ + return m_id; +} + +QByteArray WorkerDataEvent::data() const +{ + return m_data; +} + +WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) +: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) +{ +} + +int WorkerLoadEvent::workerId() const +{ + return m_id; +} + +QUrl WorkerLoadEvent::url() const +{ + return m_url; +} + +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + +WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) +: QEvent((QEvent::Type)WorkerError), m_error(error) +{ +} + +QQmlError WorkerErrorEvent::error() const +{ + return m_error; +} + +QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) +: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) +{ + d->m_lock.lock(); + connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); + start(QThread::LowestPriority); + d->m_wait.wait(&d->m_lock); + d->moveToThread(this); + d->m_lock.unlock(); +} + +QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() +{ + d->m_lock.lock(); + QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); + d->m_lock.unlock(); + + //We have to force to cleanup the main thread's event queue here + //to make sure the main GUI release all pending locks/wait conditions which + //some worker script/agent are waiting for (QQuickListModelWorkerAgent::sync() for example). + QCoreApplication::processEvents(); + wait(); + d->deleteLater(); +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() +{ + qPersistentDispose(object); +} + +int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) +{ + typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; + WorkerScript *script = new WorkerScript; + + script->id = d->m_nextId++; + script->owner = owner; + + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; +} + +void QQuickWorkerScriptEngine::removeWorkerScript(int id) +{ + QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); + if (script) { + script->owner = 0; + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); + } +} + +void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) +{ + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); +} + +void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) +{ + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); +} + +void QQuickWorkerScriptEngine::run() +{ + d->m_lock.lock(); + + d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d); + d->workerEngine->init(); + + d->m_wait.wakeAll(); + + d->m_lock.unlock(); + + exec(); + + qDeleteAll(d->workers); + d->workers.clear(); + + delete d->workerEngine; d->workerEngine = 0; +} + + +/*! + \qmlclass WorkerScript QQuickWorkerScript + \ingroup qml-utility-elements + \brief The WorkerScript element enables the use of threads in QML. + + Use WorkerScript to run operations in a new thread. + This is useful for running operations in the background so + that the main GUI thread is not blocked. + + Messages can be passed between the new thread and the parent thread + using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. + + An example: + + \snippet doc/src/snippets/qml/workerscript.qml 0 + + The above worker script specifies a JavaScript file, "script.js", that handles + the operations to be performed in the new thread. Here is \c script.js: + + \quotefile doc/src/snippets/qml/script.js + + When the user clicks anywhere within the rectangle, \c sendMessage() is + called, triggering the \tt WorkerScript.onMessage() handler in + \tt script.js. This in turn sends a reply message that is then received + by the \tt onMessage() handler of \tt myWorker. + + + \section3 Restrictions + + Since the \c WorkerScript.onMessage() function is run in a separate thread, the + JavaScript file is evaluated in a context separate from the main QML engine. This means + that unlike an ordinary JavaScript file that is imported into QML, the \c script.js + in the above example cannot access the properties, methods or other attributes + of the QML item, nor can it access any context properties set on the QML object + through QQmlContext. + + Additionally, there are restrictions on the types of values that can be passed to and + from the worker script. See the sendMessage() documentation for details. + + \sa {declarative/threading/workerscript}{WorkerScript example}, + {declarative/threading/threadedlistmodel}{Threaded ListModel example} +*/ +QQuickWorkerScript::QQuickWorkerScript(QObject *parent) +: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) +{ +} + +QQuickWorkerScript::~QQuickWorkerScript() +{ + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); +} + +/*! + \qmlproperty url WorkerScript::source + + This holds the url of the JavaScript file that implements the + \tt WorkerScript.onMessage() handler for threaded operations. +*/ +QUrl QQuickWorkerScript::source() const +{ + return m_source; +} + +void QQuickWorkerScript::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + m_source = source; + + if (engine()) + m_engine->executeUrl(m_scriptId, m_source); + + emit sourceChanged(); +} + +/*! + \qmlmethod WorkerScript::sendMessage(jsobject message) + + Sends the given \a message to a worker script handler in another + thread. The other worker script handler can receive this message + through the onMessage() handler. + + The \c message object may only contain values of the following + types: + + \list + \o boolean, number, string + \o JavaScript objects and arrays + \o ListModel objects (any other type of QObject* is not allowed) + \endlist + + All objects and arrays are copied to the \c message. With the exception + of ListModel objects, any modifications by the other thread to an object + passed in \c message will not be reflected in the original object. +*/ +void QQuickWorkerScript::sendMessage(QQmlV8Function *args) +{ + if (!engine()) { + qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); + return; + } + + v8::Handle<v8::Value> argument = v8::Undefined(); + if (args->Length() != 0) + argument = (*args)[0]; + + m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine())); +} + +void QQuickWorkerScript::classBegin() +{ + m_componentComplete = false; +} + +QQuickWorkerScriptEngine *QQuickWorkerScript::engine() +{ + if (m_engine) return m_engine; + if (m_componentComplete) { + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); + return 0; + } + + m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); + + if (m_source.isValid()) + m_engine->executeUrl(m_scriptId, m_source); + + return m_engine; + } + return 0; +} + +void QQuickWorkerScript::componentComplete() +{ + m_componentComplete = true; + engine(); // Get it started now. +} + +/*! + \qmlsignal WorkerScript::onMessage(jsobject msg) + + This handler is called when a message \a msg is received from a worker + script in another thread through a call to sendMessage(). +*/ + +bool QQuickWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QQmlEngine *engine = qmlEngine(this); + if (engine) { + WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); + QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + v8::Handle<v8::Value> value = QV8Worker::deserialize(workerEvent->data(), v8engine); + emit message(QQmlV8Handle::fromHandle(value)); + } + return true; + } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { + WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event); + QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); + return true; + } else { + return QObject::event(event); + } +} + +QT_END_NAMESPACE + +#include <qquickworkerscript.moc> + diff --git a/src/qml/qml/qquickworkerscript_p.h b/src/qml/qml/qquickworkerscript_p.h new file mode 100644 index 0000000000..fd1c3ed690 --- /dev/null +++ b/src/qml/qml/qquickworkerscript_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWORKERSCRIPT_P_H +#define QQUICKWORKERSCRIPT_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 "qqml.h" +#include "qqmlparserstatus.h" + +#include <QtCore/qthread.h> +#include <QtQml/qjsvalue.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQuickWorkerScript; +class QQuickWorkerScriptEnginePrivate; +class QQuickWorkerScriptEngine : public QThread +{ +Q_OBJECT +public: + QQuickWorkerScriptEngine(QQmlEngine *parent = 0); + virtual ~QQuickWorkerScriptEngine(); + + int registerWorkerScript(QQuickWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QByteArray &); + +protected: + virtual void run(); + +private: + QQuickWorkerScriptEnginePrivate *d; +}; + +class QQmlV8Function; +class QQmlV8Handle; +class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + + Q_INTERFACES(QQmlParserStatus) +public: + QQuickWorkerScript(QObject *parent = 0); + virtual ~QQuickWorkerScript(); + + QUrl source() const; + void setSource(const QUrl &); + +public slots: + void sendMessage(QQmlV8Function*); + +signals: + void sourceChanged(); + void message(const QQmlV8Handle &messageObject); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + virtual bool event(QEvent *); + +private: + QQuickWorkerScriptEngine *engine(); + QQuickWorkerScriptEngine *m_engine; + int m_scriptId; + QUrl m_source; + bool m_componentComplete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickWorkerScript) + +QT_END_HEADER + +#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/qml/rewriter/rewriter.pri b/src/qml/qml/rewriter/rewriter.pri new file mode 100644 index 0000000000..e51ee5ba4e --- /dev/null +++ b/src/qml/qml/rewriter/rewriter.pri @@ -0,0 +1,2 @@ +HEADERS += $$PWD/textwriter_p.h +SOURCES += $$PWD/textwriter.cpp diff --git a/src/qml/qml/rewriter/textwriter.cpp b/src/qml/qml/rewriter/textwriter.cpp new file mode 100644 index 0000000000..f14c4af521 --- /dev/null +++ b/src/qml/qml/rewriter/textwriter.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textwriter_p.h" + +QT_QML_BEGIN_NAMESPACE + +using namespace QQmlJS; + +TextWriter::TextWriter() + :string(0), cursor(0) +{ +} + +static bool overlaps(int posA, int lengthA, int posB, int lengthB) { + return (posA < posB + lengthB && posA + lengthA > posB + lengthB) + || (posA < posB && posA + lengthA > posB); +} + +bool TextWriter::hasOverlap(int pos, int length) +{ + { + QListIterator<Replace> i(replaceList); + while (i.hasNext()) { + const Replace &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + } + { + QListIterator<Move> i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + return false; + } +} + +bool TextWriter::hasMoveInto(int pos, int length) +{ + QListIterator<Move> i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (cmd.to >= pos && cmd.to < pos + length) + return true; + } + return false; +} + +void TextWriter::replace(int pos, int length, const QString &replacement) +{ + Q_ASSERT(!hasOverlap(pos, length)); + Q_ASSERT(!hasMoveInto(pos, length)); + + Replace cmd; + cmd.pos = pos; + cmd.length = length; + cmd.replacement = replacement; + replaceList += cmd; +} + +void TextWriter::move(int pos, int length, int to) +{ + Q_ASSERT(!hasOverlap(pos, length)); + + Move cmd; + cmd.pos = pos; + cmd.length = length; + cmd.to = to; + moveList += cmd; +} + +void TextWriter::doReplace(const Replace &replace) +{ + int diff = replace.replacement.size() - replace.length; + { + QMutableListIterator<Replace> i(replaceList); + while (i.hasNext()) { + Replace &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + } + } + { + QMutableListIterator<Move> i(moveList); + while (i.hasNext()) { + Move &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + + if (replace.pos < c.to) + c.to += diff; + } + } + + if (string) { + string->replace(replace.pos, replace.length, replace.replacement); + } else if (cursor) { + cursor->setPosition(replace.pos); + cursor->setPosition(replace.pos + replace.length, QTextCursor::KeepAnchor); + cursor->insertText(replace.replacement); + } +} + +void TextWriter::doMove(const Move &move) +{ + QString text; + if (string) { + text = string->mid(move.pos, move.length); + } else if (cursor) { + cursor->setPosition(move.pos); + cursor->setPosition(move.pos + move.length, QTextCursor::KeepAnchor); + text = cursor->selectedText(); + } + + Replace cut; + cut.pos = move.pos; + cut.length = move.length; + Replace paste; + paste.pos = move.to; + paste.length = 0; + paste.replacement = text; + + replaceList.append(cut); + replaceList.append(paste); + + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } +} + +void TextWriter::write(QString *s) +{ + string = s; + write_helper(); + string = 0; +} + +void TextWriter::write(QTextCursor *textCursor) +{ + cursor = textCursor; + write_helper(); + cursor = 0; +} + +void TextWriter::write_helper() +{ + if (cursor) + cursor->beginEditBlock(); + { + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } + } + { + Move cmd; + while (!moveList.isEmpty()) { + cmd = moveList.first(); + moveList.removeFirst(); + doMove(cmd); + } + } + if (cursor) + cursor->endEditBlock(); +} + +QT_QML_END_NAMESPACE diff --git a/src/qml/qml/rewriter/textwriter_p.h b/src/qml/qml/rewriter/textwriter_p.h new file mode 100644 index 0000000000..94e2d08730 --- /dev/null +++ b/src/qml/qml/rewriter/textwriter_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTWRITER_H +#define TEXTWRITER_H + +#include <private/qqmljsglobal_p.h> + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtGui/QTextCursor> + +QT_BEGIN_HEADER +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class TextWriter +{ + QString *string; + QTextCursor *cursor; + + struct Replace { + int pos; + int length; + QString replacement; + }; + + QList<Replace> replaceList; + + struct Move { + int pos; + int length; + int to; + }; + + QList<Move> moveList; + + bool hasOverlap(int pos, int length); + bool hasMoveInto(int pos, int length); + + void doReplace(const Replace &replace); + void doMove(const Move &move); + + void write_helper(); + +public: + TextWriter(); + + void replace(int pos, int length, const QString &replacement); + void move(int pos, int length, int to); + + void write(QString *s); + void write(QTextCursor *textCursor); + +}; + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE +QT_END_HEADER + +#endif // TEXTWRITER_H diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp new file mode 100644 index 0000000000..c03292d74d --- /dev/null +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -0,0 +1,1538 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define REGISTER_CLEANUP_DEBUG + +#include "qv4bindings_p.h" +#include "qv4program_p.h" +#include "qv4compiler_p.h" +#include "qv4compiler_p_p.h" + +#include <private/qqmlaccessors_p.h> +#include <private/qqmlprofilerservice_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmltrace_p.h> + +#include <QtQml/qqmlinfo.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <math.h> // ::fmod + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +namespace { +struct Register { + typedef QQmlRegisterType Type; + + void setUndefined() { dataType = UndefinedType; } + void setNaN() { setqreal(qSNaN()); } + bool isUndefined() const { return dataType == UndefinedType; } + + void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; } + QObject *getQObject() const { return qobjectValue; } + + void setqreal(qreal v) { qrealValue = v; dataType = QRealType; } + qreal getqreal() const { return qrealValue; } + qreal &getqrealref() { return qrealValue; } + + void setint(int v) { intValue = v; dataType = IntType; } + int getint() const { return intValue; } + int &getintref() { return intValue; } + + void setbool(bool v) { boolValue = v; dataType = BoolType; } + bool getbool() const { return boolValue; } + bool &getboolref() { return boolValue; } + + QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } + QString *getstringptr() { return (QString *)typeDataPtr(); } + QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } + const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } + const QString *getstringptr() const { return (QString *)typeDataPtr(); } + const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } + + void *typeDataPtr() { return (void *)&data; } + void *typeMemory() { return (void *)data; } + const void *typeDataPtr() const { return (void *)&data; } + const void *typeMemory() const { return (void *)data; } + + Type gettype() const { return dataType; } + void settype(Type t) { dataType = t; } + + Type dataType; // Type of data + union { + QObject *qobjectValue; + qreal qrealValue; + int intValue; + bool boolValue; + void *data[sizeof(QVariant)]; + qint64 q_for_alignment_1; + double q_for_alignment_2; + }; + + inline void cleanup(); + inline void cleanupString(); + inline void cleanupUrl(); + inline void cleanupVariant(); + + inline void copy(const Register &other); + inline void init(Type type); +#ifdef REGISTER_CLEANUP_DEBUG + Register() { + type = 0; + } + + ~Register() { + if (dataType >= FirstCleanupType) + qWarning("Register leaked of type %d", dataType); + } +#endif +}; + +void Register::cleanup() +{ + if (dataType >= FirstCleanupType) { + if (dataType == QStringType) { + getstringptr()->~QString(); + } else if (dataType == QUrlType) { + geturlptr()->~QUrl(); + } else if (dataType == QVariantType) { + getvariantptr()->~QVariant(); + } + } + setUndefined(); +} + +void Register::cleanupString() +{ + getstringptr()->~QString(); + setUndefined(); +} + +void Register::cleanupUrl() +{ + geturlptr()->~QUrl(); + setUndefined(); +} + +void Register::cleanupVariant() +{ + getvariantptr()->~QVariant(); + setUndefined(); +} + +void Register::copy(const Register &other) +{ + *this = other; + if (other.dataType >= FirstCleanupType) { + if (other.dataType == QStringType) + new (getstringptr()) QString(*other.getstringptr()); + else if (other.dataType == QUrlType) + new (geturlptr()) QUrl(*other.geturlptr()); + else if (other.dataType == QVariantType) + new (getvariantptr()) QVariant(*other.getvariantptr()); + } +} + +void Register::init(Type type) +{ + dataType = type; + if (dataType >= FirstCleanupType) { + if (dataType == QStringType) + new (getstringptr()) QString(); + else if (dataType == QUrlType) + new (geturlptr()) QUrl(); + else if (dataType == QVariantType) + new (getvariantptr()) QVariant(); + } +} + +} // end of anonymous namespace + +QV4Bindings::QV4Bindings(const char *programData, + QQmlContextData *context, + QQmlRefCount *ref) +: subscriptions(0), program(0), dataRef(0), bindings(0) +{ + program = (QV4Program *)programData; + dataRef = ref; + if (dataRef) dataRef->addref(); + + if (program) { + subscriptions = new Subscription[program->subscriptions]; + bindings = new Binding[program->bindings]; + + QQmlAbstractExpression::setContext(context); + } +} + +QV4Bindings::~QV4Bindings() +{ + delete [] bindings; + delete [] subscriptions; subscriptions = 0; + if (dataRef) dataRef->release(); +} + +QQmlAbstractBinding *QV4Bindings::configBinding(int index, QObject *target, + QObject *scope, int property, + int line, int column) +{ + Binding *rv = bindings + index; + + rv->index = index; + rv->property = property; + rv->target = target; + rv->scope = scope; + rv->line = line; + rv->column = column; + rv->parent = this; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +void QV4Bindings::Binding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + if (enabled != e) { + enabled = e; + + if (e) update(flags); + } +} + +void QV4Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) +{ + parent->run(this, flags); +} + +void QV4Bindings::Binding::destroy() +{ + enabled = false; + removeFromObject(); + clear(); + removeError(); + parent->release(); +} + +int QV4Bindings::Binding::propertyIndex() const +{ + return property; +} + +QObject *QV4Bindings::Binding::object() const +{ + return target; +} + +void QV4Bindings::Subscription::subscriptionCallback(QQmlNotifierEndpoint *e) +{ + Subscription *s = static_cast<Subscription *>(e); + s->bindings->subscriptionNotify(s->method); +} + +void QV4Bindings::subscriptionNotify(int id) +{ + QV4Program::BindingReferenceList *list = program->signalTable(id); + + for (quint32 ii = 0; ii < list->count; ++ii) { + QV4Program::BindingReference *bindingRef = list->bindings + ii; + + Binding *binding = bindings + bindingRef->binding; + + if (binding->executedBlocks & bindingRef->blockMask) { + run(binding, QQmlPropertyPrivate::DontRemoveBinding); + } + } +} + +void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) +{ + if (!binding->enabled) + return; + + QQmlContextData *context = QQmlAbstractExpression::context(); + if (!context || !context->isValid()) + return; + + QQmlTrace trace("V4 Binding Update"); + trace.addDetail("URL", context->url); + trace.addDetail("Line", binding->line); + trace.addDetail("Column", binding->column); + + QQmlBindingProfiler prof(context->urlString, binding->line, binding->column); + + if (binding->updating) { + QString name; + if (binding->property & 0xFFFF0000) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + Q_ASSERT(vt); + + name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name()); + name.append(QLatin1String(".")); + name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name())); + } else { + name = QLatin1String(binding->target->metaObject()->property(binding->property).name()); + } + qmlInfo(binding->target) << tr("Binding loop detected for property \"%1\"").arg(name); + return; + } + + binding->updating = true; + if (binding->property & 0xFFFF0000) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + Q_ASSERT(vt); + vt->read(binding->target, binding->property & 0xFFFF); + + QObject *target = vt; + run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags); + + vt->write(binding->target, binding->property & 0xFFFF, flags); + } else { + run(binding->index, binding->executedBlocks, context, binding, binding->scope, binding->target, flags); + } + binding->updating = false; +} + + +void QV4Bindings::unsubscribe(int subIndex) +{ + Subscription *sub = (subscriptions + subIndex); + sub->disconnect(); +} + +void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex) +{ + unsubscribe(subIndex); + + if (p->idValues[idIndex]) { + Subscription *sub = (subscriptions + subIndex); + sub->bindings = this; + sub->method = subIndex; + sub->connect(&p->idValues[idIndex].bindings); + } +} + +void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex) +{ + Subscription *sub = (subscriptions + subIndex); + if (sub->isConnected(o, notifyIndex)) + return; + sub->bindings = this; + sub->method = subIndex; + if (o) + sub->connect(o, notifyIndex); + else + sub->disconnect(); +} + +// Conversion functions - these MUST match the QtScript expression path +inline static qreal toReal(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return reg->getqreal(); + } else if (type == qMetaTypeId<QVariant>()) { + return reg->getvariantptr()->toReal(); + } else { + if (ok) *ok = false; + return 0; + } +} + +inline static QString toString(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return QString::number(reg->getqreal()); + } else if (type == QMetaType::Int) { + return QString::number(reg->getint()); + } else if (type == qMetaTypeId<QVariant>()) { + return reg->getvariantptr()->toString(); + } else if (type == QMetaType::QString) { + return *reg->getstringptr(); + } else { + if (ok) *ok = false; + return QString(); + } +} + +inline static bool toBool(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::Bool) { + return reg->getbool(); + } else if (type == qMetaTypeId<QVariant>()) { + return reg->getvariantptr()->toBool(); + } else { + if (ok) *ok = false; + return false; + } +} + +inline static QUrl toUrl(Register *reg, int type, QQmlContextData *context, bool *ok = 0) +{ + if (ok) *ok = true; + + QUrl base; + if (type == qMetaTypeId<QVariant>()) { + QVariant *var = reg->getvariantptr(); + int vt = var->type(); + if (vt == QVariant::Url) { + base = var->toUrl(); + } else if (vt == QVariant::ByteArray) { + // Preserve any valid percent-encoded octets supplied by the source + base.setEncodedUrl(var->toByteArray(), QUrl::TolerantMode); + } else if (vt == QVariant::String) { + base.setEncodedUrl(var->toString().toUtf8(), QUrl::TolerantMode); + } else { + if (ok) *ok = false; + return QUrl(); + } + } else if (type == QMetaType::QString) { + base.setEncodedUrl(reg->getstringptr()->toUtf8(), QUrl::TolerantMode); + } else { + if (ok) *ok = false; + return QUrl(); + } + + if (!base.isEmpty() && base.isRelative()) + return context->url.resolved(base); + else + return base; +} + +static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4) +{ + QVariant qtscript = qtscriptRaw; + + if (qtscript.userType() == v4.userType()) { + } else if (qtscript.canConvert((QVariant::Type)v4.userType())) { + qtscript.convert((QVariant::Type)v4.userType()); + } else if (qtscript.userType() == QVariant::Invalid && v4.userType() == QMetaType::QObjectStar) { + qtscript = qVariantFromValue<QObject *>(0); + } else { + return false; + } + + int type = qtscript.userType(); + + if (type == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { + return QQmlMetaType::QQuickAnchorLineCompare(qtscript.constData(), v4.constData()); + } else if (type == QMetaType::Double) { + + double la = qvariant_cast<double>(qtscript); + double lr = qvariant_cast<double>(v4); + + return la == lr || (qIsNaN(la) && qIsNaN(lr)); + + } else if (type == QMetaType::Float) { + + float la = qvariant_cast<float>(qtscript); + float lr = qvariant_cast<float>(v4); + + return la == lr || (qIsNaN(la) && qIsNaN(lr)); + + } else { + return qtscript == v4; + } +} + +QByteArray testResultToString(const QVariant &result, bool undefined) +{ + if (undefined) { + return "undefined"; + } else { + QString rv; + QDebug d(&rv); + d << result; + return rv.toUtf8(); + } +} + +static void testBindingResult(const QString &binding, int line, int column, + QQmlContextData *context, QObject *scope, + const Register &result, int resultType) +{ + QQmlExpression expression(context->asQQmlContext(), scope, binding); + bool isUndefined = false; + QVariant value = expression.evaluate(&isUndefined); + + bool iserror = false; + QByteArray qtscriptResult; + QByteArray v4Result; + + if (expression.hasError()) { + iserror = true; + qtscriptResult = "exception"; + } else { + qtscriptResult = testResultToString(value, isUndefined); + } + + if (isUndefined && result.isUndefined()) { + return; + } else if(isUndefined != result.isUndefined()) { + iserror = true; + } + + QVariant v4value; + if (!result.isUndefined()) { + switch (resultType) { + case QMetaType::QString: + v4value = *result.getstringptr(); + break; + case QMetaType::QUrl: + v4value = *result.geturlptr(); + break; + case QMetaType::QObjectStar: + v4value = qVariantFromValue<QObject *>(result.getQObject()); + break; + case QMetaType::Bool: + v4value = result.getbool(); + break; + case QMetaType::Int: + v4value = result.getint(); + break; + case QMetaType::QReal: + v4value = result.getqreal(); + break; + default: + if (resultType == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { + v4value = QVariant(QQmlMetaType::QQuickAnchorLineMetaTypeId(), result.typeDataPtr()); + } else { + iserror = true; + v4Result = "Unknown V4 type"; + } + } + } + if (v4Result.isEmpty()) + v4Result = testResultToString(v4value, result.isUndefined()); + + if (!testCompareVariants(value, v4value)) + iserror = true; + + if (iserror) { + qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ":" << line << ":" << column; + + qWarning().nospace() << " Binding: " << binding; + qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); + qWarning().nospace() << " V4: " << v4Result.constData(); + } +} + +static void testBindingException(const QString &binding, int line, int column, + QQmlContextData *context, QObject *scope) +{ + QQmlExpression expression(context->asQQmlContext(), scope, binding); + bool isUndefined = false; + QVariant value = expression.evaluate(&isUndefined); + + if (!expression.hasError()) { + QByteArray qtscriptResult = testResultToString(value, isUndefined); + qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ":" << line << ":" << column; + qWarning().nospace() << " Binding: " << binding; + qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); + qWarning().nospace() << " V4: exception"; + } +} + +static void throwException(int id, QQmlDelayedError *error, + QV4Program *program, QQmlContextData *context, + const QString &description = QString()) +{ + error->error.setUrl(context->url); + if (description.isEmpty()) + error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object")); + else + error->error.setDescription(description); + if (id != 0xFF) { + quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); + error->error.setLine((e >> 32) & 0xFFFFFFFF); + error->error.setColumn(e & 0xFFFFFFFF); + } else { + error->error.setLine(-1); + error->error.setColumn(-1); + } + if (!context->engine || !error->addError(QQmlEnginePrivate::get(context->engine))) + QQmlEnginePrivate::warning(context->engine, error->error); +} + +const qreal QV4Bindings::D32 = 4294967296.0; + +qint32 QV4Bindings::toInt32(qreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + double sign = (n < 0) ? -1.0 : 1.0; + qreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + const double D31 = D32 / 2.0; + + if (sign == -1 && n < -D31) + n += D32; + + else if (sign != -1 && n >= D31) + n -= D32; + + return qint32 (n); +} + +inline quint32 QV4Bindings::toUint32(qreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + double sign = (n < 0) ? -1.0 : 1.0; + qreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + + if (n < 0) + n += D32; + + return quint32 (n); +} + +#define THROW_EXCEPTION_STR(id, str) { \ + if (testBinding) testBindingException(*testBindingSource, bindingLine, bindingColumn, context, scope); \ + throwException((id), error, program, context, (str)); \ + goto exceptionExit; \ +} + +#define THROW_EXCEPTION(id) THROW_EXCEPTION_STR(id, QString()) + +#define MARK_REGISTER(reg) cleanupRegisterMask |= (1 << (reg)) +#define MARK_CLEAN_REGISTER(reg) cleanupRegisterMask &= ~(1 << (reg)) + +#define STRING_REGISTER(reg) { \ + registers[(reg)].settype(QStringType); \ + MARK_REGISTER(reg); \ +} + +#define URL_REGISTER(reg) { \ + registers[(reg)].settype(QUrlType); \ + MARK_REGISTER(reg); \ +} + +#define VARIANT_REGISTER(reg) { \ + registers[(reg)].settype(QVariantType); \ + MARK_REGISTER(reg); \ +} + +#ifdef QML_THREADED_INTERPRETER +void **QV4Bindings::getDecodeInstrTable() +{ + static void **decode_instr; + if (!decode_instr) { + QV4Bindings *dummy = new QV4Bindings(0, 0, 0); + quint32 executedBlocks = 0; + dummy->run(0, executedBlocks, 0, 0, 0, 0, + QQmlPropertyPrivate::BypassInterceptor, + &decode_instr); + dummy->release(); + } + return decode_instr; +} +#endif + +void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, + QQmlContextData *context, QQmlDelayedError *error, + QObject *scope, QObject *output, + QQmlPropertyPrivate::WriteFlags storeFlags +#ifdef QML_THREADED_INTERPRETER + ,void ***table +#endif + ) +{ +#ifdef QML_THREADED_INTERPRETER + if (table) { + static void *decode_instr[] = { + FOR_EACH_V4_INSTR(QML_V4_INSTR_ADDR) + }; + + *table = decode_instr; + return; + } +#endif + + + error->removeError(); + + Register registers[32]; + quint32 cleanupRegisterMask = 0; + + executedBlocks = 0; + + const char *code = program->instructions(); + code += instrIndex * QML_V4_INSTR_SIZE(Jump, jump); + const V4Instr *instr = reinterpret_cast<const V4Instr *>(code); + + const char *data = program->data(); + + QString *testBindingSource = 0; + bool testBinding = false; + int bindingLine = 0; + int bindingColumn = 0; + +#ifdef QML_THREADED_INTERPRETER + goto *instr->common.code; +#else + for (;;) { + switch (instr->common.type) { +#endif + + QML_V4_BEGIN_INSTR(Noop, common) + QML_V4_END_INSTR(Noop, common) + + QML_V4_BEGIN_INSTR(BindingId, id) + bindingLine = instr->id.line; + bindingColumn = instr->id.column; + QML_V4_END_INSTR(BindingId, id) + + QML_V4_BEGIN_INSTR(SubscribeId, subscribeop) + subscribeId(context, instr->subscribeop.index, instr->subscribeop.offset); + QML_V4_END_INSTR(SubscribeId, subscribeop) + + QML_V4_BEGIN_INSTR(Subscribe, subscribeop) + { + QObject *o = 0; + const Register &object = registers[instr->subscribeop.reg]; + if (!object.isUndefined()) o = object.getQObject(); + subscribe(o, instr->subscribeop.index, instr->subscribeop.offset); + } + QML_V4_END_INSTR(Subscribe, subscribeop) + + QML_V4_BEGIN_INSTR(FetchAndSubscribe, fetchAndSubscribe) + { + Register ® = registers[instr->fetchAndSubscribe.reg]; + + if (reg.isUndefined()) + THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId); + + QObject *object = reg.getQObject(); + if (!object) { + reg.setUndefined(); + } else { + int subIdx = instr->fetchAndSubscribe.subscription; + Subscription *sub = 0; + if (subIdx != -1) { + sub = (subscriptions + subIdx); + sub->bindings = this; + sub->method = subIdx; + } + reg.init((Register::Type)instr->fetchAndSubscribe.valueType); + if (instr->fetchAndSubscribe.valueType >= FirstCleanupType) + MARK_REGISTER(instr->fetchAndSubscribe.reg); + QQmlAccessors *accessors = instr->fetchAndSubscribe.property.accessors; + accessors->read(object, instr->fetchAndSubscribe.property.accessorData, + reg.typeDataPtr()); + + if (accessors->notifier) { + QQmlNotifier *notifier = 0; + accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, ¬ifier); + if (notifier) sub->connect(notifier); + } else if (instr->fetchAndSubscribe.property.notifyIndex != -1) { + sub->connect(object, instr->fetchAndSubscribe.property.notifyIndex); + } + } + } + QML_V4_END_INSTR(FetchAndSubscribe, fetchAndSubscribe) + + QML_V4_BEGIN_INSTR(LoadId, load) + registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); + QML_V4_END_INSTR(LoadId, load) + + QML_V4_BEGIN_INSTR(LoadScope, load) + registers[instr->load.reg].setQObject(scope); + QML_V4_END_INSTR(LoadScope, load) + + QML_V4_BEGIN_INSTR(LoadRoot, load) + registers[instr->load.reg].setQObject(context->contextObject); + QML_V4_END_INSTR(LoadRoot, load) + + QML_V4_BEGIN_INSTR(LoadAttached, attached) + { + const Register &input = registers[instr->attached.reg]; + Register &output = registers[instr->attached.output]; + if (input.isUndefined()) + THROW_EXCEPTION(instr->attached.exceptionId); + + QObject *object = registers[instr->attached.reg].getQObject(); + if (!object) { + output.setUndefined(); + } else { + QObject *attached = qmlAttachedPropertiesObjectById(instr->attached.id, input.getQObject(), true); + Q_ASSERT(attached); + output.setQObject(attached); + } + } + QML_V4_END_INSTR(LoadAttached, attached) + + QML_V4_BEGIN_INSTR(UnaryNot, unaryop) + { + registers[instr->unaryop.output].setbool(!registers[instr->unaryop.src].getbool()); + } + QML_V4_END_INSTR(UnaryNot, unaryop) + + QML_V4_BEGIN_INSTR(UnaryMinusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(-registers[instr->unaryop.src].getqreal()); + } + QML_V4_END_INSTR(UnaryMinusReal, unaryop) + + QML_V4_BEGIN_INSTR(UnaryMinusInt, unaryop) + { + registers[instr->unaryop.output].setint(-registers[instr->unaryop.src].getint()); + } + QML_V4_END_INSTR(UnaryMinusInt, unaryop) + + QML_V4_BEGIN_INSTR(UnaryPlusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(+registers[instr->unaryop.src].getqreal()); + } + QML_V4_END_INSTR(UnaryPlusReal, unaryop) + + QML_V4_BEGIN_INSTR(UnaryPlusInt, unaryop) + { + registers[instr->unaryop.output].setint(+registers[instr->unaryop.src].getint()); + } + QML_V4_END_INSTR(UnaryPlusInt, unaryop) + + QML_V4_BEGIN_INSTR(ConvertBoolToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(src.getbool()); + } + QML_V4_END_INSTR(ConvertBoolToInt, unaryop) + + QML_V4_BEGIN_INSTR(ConvertBoolToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(src.getbool()); + } + QML_V4_END_INSTR(ConvertBoolToReal, unaryop) + + QML_V4_BEGIN_INSTR(ConvertBoolToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QLatin1String(src.getbool() ? "true" : "false")); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_V4_END_INSTR(ConvertBoolToString, unaryop) + + QML_V4_BEGIN_INSTR(ConvertIntToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setbool(src.getint()); + } + QML_V4_END_INSTR(ConvertIntToBool, unaryop) + + QML_V4_BEGIN_INSTR(ConvertIntToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qreal(src.getint())); + } + QML_V4_END_INSTR(ConvertIntToReal, unaryop) + + QML_V4_BEGIN_INSTR(ConvertIntToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QString::number(src.getint())); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_V4_END_INSTR(ConvertIntToString, unaryop) + + QML_V4_BEGIN_INSTR(ConvertRealToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setbool(src.getqreal() != 0); + } + QML_V4_END_INSTR(ConvertRealToBool, unaryop) + + QML_V4_BEGIN_INSTR(ConvertRealToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(toInt32(src.getqreal())); + } + QML_V4_END_INSTR(ConvertRealToInt, unaryop) + + QML_V4_BEGIN_INSTR(ConvertRealToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QString::number(src.getqreal())); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_V4_END_INSTR(ConvertRealToString, unaryop) + + QML_V4_BEGIN_INSTR(ConvertStringToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QJSValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setbool(tmp.toBool()); + } + } + QML_V4_END_INSTR(ConvertStringToBool, unaryop) + + QML_V4_BEGIN_INSTR(ConvertStringToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QJSValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setint(tmp.toInt()); + } + } + QML_V4_END_INSTR(ConvertStringToInt, unaryop) + + QML_V4_BEGIN_INSTR(ConvertStringToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QJSValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setqreal(tmp.toNumber()); + } + } + QML_V4_END_INSTR(ConvertStringToReal, unaryop) + + QML_V4_BEGIN_INSTR(ConvertStringToUrl, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + const QString tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + QUrl *urlPtr = output.geturlptr(); + new (urlPtr) QUrl(); + urlPtr->setEncodedUrl(tmp.toUtf8(), QUrl::TolerantMode); + + URL_REGISTER(instr->unaryop.output); + } + } + QML_V4_END_INSTR(ConvertStringToUrl, unaryop) + + QML_V4_BEGIN_INSTR(ConvertUrlToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + const QUrl tmp(*src.geturlptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupUrl(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setbool(!tmp.isEmpty()); + } + } + QML_V4_END_INSTR(ConvertUrlToBool, unaryop) + + QML_V4_BEGIN_INSTR(ConvertUrlToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + const QUrl tmp(*src.geturlptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupUrl(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + new (output.getstringptr()) QString(tmp.toString()); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_V4_END_INSTR(ConvertUrlToString, unaryop) + + QML_V4_BEGIN_INSTR(ResolveUrl, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) { + output.setUndefined(); + } else { + const QUrl tmp(*src.geturlptr()); + if (instr->unaryop.src == instr->unaryop.output) { + *output.geturlptr() = context->resolvedUrl(tmp); + } else { + new (output.geturlptr()) QUrl(context->resolvedUrl(tmp)); + URL_REGISTER(instr->unaryop.output); + } + } + } + QML_V4_END_INSTR(ResolveUrl, unaryop) + + QML_V4_BEGIN_INSTR(MathSinReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qSin(src.getqreal())); + } + QML_V4_END_INSTR(MathSinReal, unaryop) + + QML_V4_BEGIN_INSTR(MathCosReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qCos(src.getqreal())); + } + QML_V4_END_INSTR(MathCosReal, unaryop) + + QML_V4_BEGIN_INSTR(MathRoundReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(qRound(src.getqreal())); + } + QML_V4_END_INSTR(MathRoundReal, unaryop) + + QML_V4_BEGIN_INSTR(MathFloorReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(qFloor(src.getqreal())); + } + QML_V4_END_INSTR(MathFloorReal, unaryop) + + QML_V4_BEGIN_INSTR(MathPIReal, unaryop) + { + static const qreal qmlPI = 2.0 * qAsin(1.0); + Register &output = registers[instr->unaryop.output]; + output.setqreal(qmlPI); + } + QML_V4_END_INSTR(MathPIReal, unaryop) + + QML_V4_BEGIN_INSTR(LoadReal, real_value) + registers[instr->real_value.reg].setqreal(instr->real_value.value); + QML_V4_END_INSTR(LoadReal, real_value) + + QML_V4_BEGIN_INSTR(LoadInt, int_value) + registers[instr->int_value.reg].setint(instr->int_value.value); + QML_V4_END_INSTR(LoadInt, int_value) + + QML_V4_BEGIN_INSTR(LoadBool, bool_value) + registers[instr->bool_value.reg].setbool(instr->bool_value.value); + QML_V4_END_INSTR(LoadBool, bool_value) + + QML_V4_BEGIN_INSTR(LoadString, string_value) + { + Register &output = registers[instr->string_value.reg]; + QChar *string = (QChar *)(data + instr->string_value.offset); + new (output.getstringptr()) QString(string, instr->string_value.length); + STRING_REGISTER(instr->string_value.reg); + } + QML_V4_END_INSTR(LoadString, string_value) + + QML_V4_BEGIN_INSTR(EnableV4Test, string_value) + { + testBindingSource = new QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + testBinding = true; + } + QML_V4_END_INSTR(String, string_value) + + QML_V4_BEGIN_INSTR(BitAndInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() & + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitAndInt, binaryop) + + QML_V4_BEGIN_INSTR(BitOrInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() | + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitAndInt, binaryop) + + QML_V4_BEGIN_INSTR(BitXorInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() ^ + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitXorInt, binaryop) + + QML_V4_BEGIN_INSTR(AddReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() + + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(AddReal, binaryop) + + QML_V4_BEGIN_INSTR(AddString, binaryop) + { + QString &string = *registers[instr->binaryop.output].getstringptr(); + if (instr->binaryop.output == instr->binaryop.left) { + string += registers[instr->binaryop.right].getstringptr(); + } else { + string = *registers[instr->binaryop.left].getstringptr() + + *registers[instr->binaryop.right].getstringptr(); + } + } + QML_V4_END_INSTR(AddString, binaryop) + + QML_V4_BEGIN_INSTR(SubReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() - + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(SubReal, binaryop) + + QML_V4_BEGIN_INSTR(MulReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() * + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(MulReal, binaryop) + + QML_V4_BEGIN_INSTR(DivReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() / + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(DivReal, binaryop) + + QML_V4_BEGIN_INSTR(ModReal, binaryop) + { + Register &target = registers[instr->binaryop.output]; + const Register &left = registers[instr->binaryop.left]; + const Register &right = registers[instr->binaryop.right]; + if (QMetaType::QReal == QMetaType::Float) + target.setqreal(::fmodf(left.getqreal(), right.getqreal())); + else + target.setqreal(::fmod(left.getqreal(), right.getqreal())); + } + QML_V4_END_INSTR(ModInt, binaryop) + + QML_V4_BEGIN_INSTR(LShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() << + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(LShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(RShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(RShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(URShiftInt, binaryop) + { + registers[instr->binaryop.output].setint((unsigned)registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(URShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(GtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() > + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(GtReal, binaryop) + + QML_V4_BEGIN_INSTR(LtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() < + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(LtReal, binaryop) + + QML_V4_BEGIN_INSTR(GeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() >= + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(GeReal, binaryop) + + QML_V4_BEGIN_INSTR(LeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() <= + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(LeReal, binaryop) + + QML_V4_BEGIN_INSTR(EqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(EqualReal, binaryop) + + QML_V4_BEGIN_INSTR(NotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(NotEqualReal, binaryop) + + QML_V4_BEGIN_INSTR(StrictEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(StrictEqualReal, binaryop) + + QML_V4_BEGIN_INSTR(StrictNotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(StrictNotEqualReal, binaryop) + + QML_V4_BEGIN_INSTR(GtString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a > b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(GtString, binaryop) + + QML_V4_BEGIN_INSTR(LtString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a < b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(LtString, binaryop) + + QML_V4_BEGIN_INSTR(GeString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a >= b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(GeString, binaryop) + + QML_V4_BEGIN_INSTR(LeString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a <= b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(LeString, binaryop) + + QML_V4_BEGIN_INSTR(EqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a == b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(EqualString, binaryop) + + QML_V4_BEGIN_INSTR(NotEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a != b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(NotEqualString, binaryop) + + QML_V4_BEGIN_INSTR(StrictEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a == b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(StrictEqualString, binaryop) + + QML_V4_BEGIN_INSTR(StrictNotEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a != b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_V4_END_INSTR(StrictNotEqualString, binaryop) + + QML_V4_BEGIN_INSTR(NewString, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + STRING_REGISTER(instr->construct.reg); + } + QML_V4_END_INSTR(NewString, construct) + + QML_V4_BEGIN_INSTR(NewUrl, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + URL_REGISTER(instr->construct.reg); + } + QML_V4_END_INSTR(NewUrl, construct) + + QML_V4_BEGIN_INSTR(Fetch, fetch) + { + Register ® = registers[instr->fetch.reg]; + + if (reg.isUndefined()) + THROW_EXCEPTION(instr->fetch.exceptionId); + + QObject *object = reg.getQObject(); + if (!object) { + THROW_EXCEPTION(instr->fetch.exceptionId); + } else { + reg.init((Register::Type)instr->fetch.valueType); + if (instr->fetch.valueType >= FirstCleanupType) + MARK_REGISTER(instr->fetch.reg); + void *argv[] = { reg.typeDataPtr(), 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + } + } + QML_V4_END_INSTR(Fetch, fetch) + + QML_V4_BEGIN_INSTR(TestV4Store, storetest) + { + Register &data = registers[instr->storetest.reg]; + testBindingResult(*testBindingSource, bindingLine, bindingColumn, context, + scope, data, instr->storetest.regType); + } + QML_V4_END_INSTR(TestV4Store, storetest) + + QML_V4_BEGIN_INSTR(Store, store) + { + Register &data = registers[instr->store.reg]; + + if (data.isUndefined()) + THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value")); + + int status = -1; + void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; + QMetaObject::metacall(output, QMetaObject::WriteProperty, + instr->store.index, argv); + + goto programExit; + } + QML_V4_END_INSTR(Store, store) + + QML_V4_BEGIN_INSTR(Copy, copy) + registers[instr->copy.reg].copy(registers[instr->copy.src]); + if (registers[instr->copy.reg].gettype() >= FirstCleanupType) + MARK_REGISTER(instr->copy.reg); + QML_V4_END_INSTR(Copy, copy) + + QML_V4_BEGIN_INSTR(Jump, jump) + if (instr->jump.reg == -1 || !registers[instr->jump.reg].getbool()) + code += instr->jump.count; + QML_V4_END_INSTR(Jump, jump) + + QML_V4_BEGIN_INSTR(BranchTrue, branchop) + if (registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_V4_END_INSTR(BranchTrue, branchop) + + QML_V4_BEGIN_INSTR(BranchFalse, branchop) + if (! registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_V4_END_INSTR(BranchFalse, branchop) + + QML_V4_BEGIN_INSTR(Branch, branchop) + code += instr->branchop.offset; + QML_V4_END_INSTR(Branch, branchop) + + QML_V4_BEGIN_INSTR(Block, blockop) + executedBlocks |= instr->blockop.block; + QML_V4_END_INSTR(Block, blockop) + + // XXX not applicable in v8 + QML_V4_BEGIN_INSTR(InitString, initstring) +// if (!identifiers[instr->initstring.offset].identifier) { +// quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); +// QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); + +// QString str = QString::fromRawData(strdata, len); + +// // identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); +// } + QML_V4_END_INSTR(InitString, initstring) + + QML_V4_BEGIN_INSTR(CleanupRegister, cleanup) + registers[instr->cleanup.reg].cleanup(); + QML_V4_END_INSTR(CleanupRegister, cleanup) + +#ifdef QML_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QV4: Unknown instruction %d encountered.", instr->common.type); + break; + } // switch + + } // while +#endif + + Q_ASSERT(!"Unreachable code reached"); + +programExit: +exceptionExit: + delete testBindingSource; + + int reg = 0; + while (cleanupRegisterMask) { + if (cleanupRegisterMask & 0x1) + registers[reg].cleanup(); + + reg++; + cleanupRegisterMask >>= 1; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h new file mode 100644 index 0000000000..61d29a6f57 --- /dev/null +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4BINDINGS_P_H +#define QV4BINDINGS_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/qqmlexpression_p.h" +#include "private/qqmlbinding_p.h" +#include "private/qv4instruction_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct QV4Program; +class QV4Bindings : public QQmlAbstractExpression, + public QQmlRefCount +{ + Q_DECLARE_TR_FUNCTIONS(QV4Bindings) +public: + QV4Bindings(const char *program, QQmlContextData *context, + QQmlRefCount *); + virtual ~QV4Bindings(); + + QQmlAbstractBinding *configBinding(int index, QObject *target, + QObject *scope, int property, + int line, int column); + +#ifdef QML_THREADED_INTERPRETER + static void **getDecodeInstrTable(); +#endif + +private: + Q_DISABLE_COPY(QV4Bindings) + + struct Binding : public QQmlAbstractBinding, public QQmlDelayedError { + Binding() : enabled(false), updating(0), property(0), + scope(0), target(0), executedBlocks(0), parent(0) {} + + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); + virtual void update(QQmlPropertyPrivate::WriteFlags flags); + virtual void destroy(); + virtual int propertyIndex() const; + virtual QObject *object() const; + + int index:30; + bool enabled:1; + bool updating:1; + int property; + QObject *scope; + int line; + int column; + QObject *target; + quint32 executedBlocks; + + QV4Bindings *parent; + }; + + class Subscription : public QQmlNotifierEndpoint + { + public: + Subscription() : bindings(0), method(-1) { callback = &subscriptionCallback; } + static void subscriptionCallback(QQmlNotifierEndpoint *e); + QV4Bindings *bindings; + int method; + }; + friend class Subscription; + + Subscription *subscriptions; + + void subscriptionNotify(int); + void run(Binding *, QQmlPropertyPrivate::WriteFlags flags); + + QV4Program *program; + QQmlRefCount *dataRef; + Binding *bindings; + + void init(); + void run(int instr, quint32 &executedBlocks, QQmlContextData *context, + QQmlDelayedError *error, QObject *scope, QObject *output, + QQmlPropertyPrivate::WriteFlags storeFlags +#ifdef QML_THREADED_INTERPRETER + , void ***decode_instr = 0 +#endif + ); + + + inline void unsubscribe(int subIndex); + inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex); + inline void subscribe(QObject *o, int notifyIndex, int subIndex); + + inline static qint32 toInt32(qreal n); + static const qreal D32; + static quint32 toUint32(qreal n); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4BINDINGS_P_H + diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp new file mode 100644 index 0000000000..d61fd580c7 --- /dev/null +++ b/src/qml/qml/v4/qv4compiler.cpp @@ -0,0 +1,1399 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4compiler_p.h" +#include "qv4compiler_p_p.h" +#include "qv4program_p.h" +#include "qv4ir_p.h" +#include "qv4irbuilder_p.h" + +#include <private/qqmlglobal_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmlaccessors_p.h> +#include <private/qqmljsengine_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP) +DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER) +DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL) +DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) +DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST) + +static bool qmlBindingsTest = false; +static bool qmlEnableV4 = true; + +using namespace QQmlJS; +QV4CompilerPrivate::QV4CompilerPrivate() +: _function(0) , _block(0) , _discarded(false) +{ +} + +// +// tracing +// +void QV4CompilerPrivate::trace(int line, int column) +{ + bytecode.clear(); + + this->currentReg = _function->tempCount; + + foreach (IR::BasicBlock *bb, _function->basicBlocks) { + if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size()) + bb->JUMP(_function->basicBlocks.at(bb->index + 1)); + } + + QVector<IR::BasicBlock *> blocks; + trace(&blocks); + currentBlockMask = 0x00000001; + + + for (int i = 0; !_discarded && i < blocks.size(); ++i) { + IR::BasicBlock *block = blocks.at(i); + IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0; + if (IR::Stmt *terminator = block->terminator()) { + if (IR::CJump *cj = terminator->asCJump()) { + if (cj->iffalse != next) { + IR::Jump *jump = _function->pool->New<IR::Jump>(); + jump->init(cj->iffalse); + block->statements.append(jump); + } + } else if (IR::Jump *j = terminator->asJump()) { + if (j->target == next) { + block->statements.resize(block->statements.size() - 1); + } + } + } + + block->offset = bytecode.size(); + + if (bytecode.isEmpty()) { + if (qmlBindingsTest || bindingsDump()) { + Instr::BindingId id; + id.column = column; + id.line = line; + gen(id); + } + + if (qmlBindingsTest) { + QString str = expression->expression.asScript(); + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + Instr::EnableV4Test test; + test.reg = 0; + test.offset = offset; + test.length = str.length(); + gen(test); + } + } + + bool usic = false; + int patchesCount = patches.count(); + qSwap(usedSubscriptionIdsChanged, usic); + + int blockopIndex = bytecode.size(); + Instr::Block blockop; + blockop.block = currentBlockMask; + gen(blockop); + + foreach (IR::Stmt *s, block->statements) { + if (! _discarded) + s->accept(this); + } + + qSwap(usedSubscriptionIdsChanged, usic); + + if (usic) { + if (currentBlockMask == 0x80000000) { + discard(); + return; + } + currentBlockMask <<= 1; + } else if (! _discarded) { + const int adjust = bytecode.remove(blockopIndex); + // Correct patches + for (int ii = patchesCount; ii < patches.count(); ++ii) + patches[ii].offset -= adjust; + } + } + +#ifdef DEBUG_IR_STRUCTURE + IR::IRDump dump; + for (int i = 0; i < blocks.size(); ++i) { + dump.basicblock(blocks.at(i)); + } +#endif + + + if (! _discarded) { + // back patching + foreach (const Patch &patch, patches) { + V4Instr &instr = bytecode[patch.offset]; + int size = V4Instr::size(instructionType(&instr)); + instr.branchop.offset = patch.block->offset - patch.offset - size; + } + + patches.clear(); + } +} + +void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks) +{ + for (int i = 0; i < _function->basicBlocks.size(); ++i) { + IR::BasicBlock *block = _function->basicBlocks.at(i); + + while (! blocks->contains(block)) { + blocks->append(block); + + if (IR::Stmt *terminator = block->terminator()) { + if (IR::CJump *cj = terminator->asCJump()) + block = cj->iffalse; + else if (IR::Jump *j = terminator->asJump()) + block = j->target; + } + } + } +} + +void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r) +{ + if (!e) { + discard(); + } else { + qSwap(currentReg, r); + e->accept(this); + qSwap(currentReg, r); + } +} + +// +// expressions +// +void QV4CompilerPrivate::visitConst(IR::Const *e) +{ + switch (e->type) { + case IR::BoolType: { + Instr::LoadBool i; + i.reg = currentReg; + i.value = e->value; + gen(i); + } break; + + case IR::IntType: { + Instr::LoadInt i; + i.reg = currentReg; + i.value = e->value; + gen(i); + } break; + + case IR::RealType: { + Instr::LoadReal i; + i.reg = currentReg; + i.value = e->value; + gen(i); + } break; + + default: + if (qmlVerboseCompiler()) + qWarning() << Q_FUNC_INFO << "unexpected type"; + discard(); + } +} + +void QV4CompilerPrivate::visitString(IR::String *e) +{ + registerLiteralString(currentReg, e->value); +} + +void QV4CompilerPrivate::visitName(IR::Name *e) +{ + if (e->base) { + // fetch the object and store it in reg. + traceExpression(e->base, currentReg); + } else { + _subscribeName.clear(); + } + + if (e->storage == IR::Name::RootStorage) { + + Instr::LoadRoot instr; + instr.reg = currentReg; + gen(instr); + + if (e->symbol == IR::Name::IdObject) { + // The ID is a reference to the root object + return; + } + + } else if (e->storage == IR::Name::ScopeStorage) { + + Instr::LoadScope instr; + instr.reg = currentReg; + gen(instr); + + _subscribeName << contextName(); + + } else if (e->storage == IR::Name::IdStorage) { + + Instr::LoadId instr; + instr.reg = currentReg; + instr.index = e->idObject->idIndex; + gen(instr); + + _subscribeName << QLatin1String("$$$ID_") + *e->id; + + if (blockNeedsSubscription(_subscribeName)) { + Instr::SubscribeId sub; + sub.reg = currentReg; + sub.offset = subscriptionIndex(_subscribeName); + sub.index = instr.index; + gen(sub); + } + + return; + } else { + // No action needed + } + + switch (e->symbol) { + case IR::Name::Unbound: + case IR::Name::IdObject: + case IR::Name::Slot: + case IR::Name::Object: { + Q_ASSERT(!"Unreachable"); + discard(); + } break; + + case IR::Name::AttachType: { + _subscribeName << *e->id; + + Instr::LoadAttached attached; + attached.output = currentReg; + attached.reg = currentReg; + attached.exceptionId = exceptionId(e->line, e->column); + if (e->declarativeType->attachedPropertiesId() == -1) + discard(); + attached.id = e->declarativeType->attachedPropertiesId(); + gen(attached); + } break; + + case IR::Name::Property: { + _subscribeName << *e->id; + + if (e->property->coreIndex == -1) { + QMetaProperty prop; + e->property->load(prop, QQmlEnginePrivate::get(engine)); + } + + const int propTy = e->property->propType; + QQmlRegisterType regType; + + switch (propTy) { + case QMetaType::QReal: + regType = QRealType; + break; + case QMetaType::Bool: + regType = BoolType; + break; + case QMetaType::Int: + regType = IntType; + break; + case QMetaType::QString: + regType = QStringType; + break; + case QMetaType::QUrl: + regType = QUrlType; + break; + + default: + if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { + regType = PODValueType; + } else if (QQmlMetaType::isQObject(propTy)) { + regType = QObjectStarType; + } else { + if (qmlVerboseCompiler()) + qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy); + discard(); // Unsupported type + return; + } + + break; + } // switch + + if (e->property->hasAccessors()) { + Instr::FetchAndSubscribe fetch; + fetch.reg = currentReg; + fetch.subscription = subscriptionIndex(_subscribeName); + fetch.exceptionId = exceptionId(e->line, e->column); + fetch.valueType = regType; + fetch.property = *e->property; + gen(fetch); + } else { + if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) { + Instr::Subscribe sub; + sub.reg = currentReg; + sub.offset = subscriptionIndex(_subscribeName); + sub.index = e->property->notifyIndex; + gen(sub); + } + + Instr::Fetch fetch; + fetch.reg = currentReg; + fetch.index = e->property->coreIndex; + fetch.exceptionId = exceptionId(e->line, e->column); + fetch.valueType = regType; + gen(fetch); + } + + } break; + } // switch +} + +void QV4CompilerPrivate::visitTemp(IR::Temp *e) +{ + if (currentReg != e->index) { + Instr::Copy i; + i.reg = currentReg; + i.src = e->index; + gen(i); + } +} + +void QV4CompilerPrivate::visitUnop(IR::Unop *e) +{ + quint8 src = currentReg; + + if (IR::Temp *temp = e->expr->asTemp()) { + src = temp->index; + } else { + traceExpression(e->expr, src); + } + + switch (e->op) { + case IR::OpInvalid: + Q_ASSERT(!"unreachable"); + break; + + case IR::OpIfTrue: + convertToBool(e->expr, src); + if (src != currentReg) { + Instr::Copy i; + i.reg = currentReg; + i.src = src; + gen(i); + } + break; + + case IR::OpNot: { + Instr::UnaryNot i; + convertToBool(e->expr, src); + i.output = currentReg; + i.src = src; + gen(i); + } break; + + case IR::OpUMinus: + if (e->expr->type == IR::RealType) { + Instr::UnaryMinusReal i; + i.output = currentReg; + i.src = src; + gen(i); + } else if (e->expr->type == IR::IntType) { + convertToReal(e->expr, currentReg); + Instr::UnaryMinusReal i; + i.output = currentReg; + i.src = src; + gen(i); + } else { + discard(); + } + break; + + case IR::OpUPlus: + if (e->expr->type == IR::RealType) { + Instr::UnaryPlusReal i; + i.output = currentReg; + i.src = src; + gen(i); + } else if (e->expr->type == IR::IntType) { + convertToReal(e->expr, currentReg); + Instr::UnaryPlusReal i; + i.output = currentReg; + i.src = src; + gen(i); + } else { + discard(); + } + break; + + case IR::OpCompl: + // TODO + discard(); + break; + + case IR::OpBitAnd: + case IR::OpBitOr: + case IR::OpBitXor: + case IR::OpAdd: + case IR::OpSub: + case IR::OpMul: + case IR::OpDiv: + case IR::OpMod: + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + case IR::OpGt: + case IR::OpLt: + case IR::OpGe: + case IR::OpLe: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + case IR::OpAnd: + case IR::OpOr: + Q_ASSERT(!"unreachable"); + break; + } // switch +} + +void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg) +{ + if (expr->type == IR::RealType) + return; + + switch (expr->type) { + case IR::BoolType: { + Instr::ConvertBoolToReal i; + i.output = i.src = reg; + gen(i); + } break; + + case IR::IntType: { + Instr::ConvertIntToReal i; + i.output = i.src = reg; + gen(i); + } break; + + case IR::RealType: + // nothing to do + return; + + default: + discard(); + break; + } // switch +} + +void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg) +{ + if (expr->type == IR::IntType) + return; + + switch (expr->type) { + case IR::BoolType: { + Instr::ConvertBoolToInt i; + i.output = i.src = reg; + gen(i); + } break; + + case IR::IntType: + // nothing to do + return; + + case IR::RealType: { + Instr::ConvertRealToInt i; + i.output = i.src = reg; + gen(i); + } break; + + default: + discard(); + break; + } // switch +} + +void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg) +{ + if (expr->type == IR::BoolType) + return; + + switch (expr->type) { + case IR::BoolType: + // nothing to do + break; + + case IR::IntType: { + Instr::ConvertIntToBool i; + i.output = i.src = reg; + gen(i); + } break; + + case IR::RealType: { + Instr::ConvertRealToBool i; + i.output = i.src = reg; + gen(i); + } return; + + case IR::StringType: { + Instr::ConvertStringToBool i; + i.output = i.src = reg; + gen(i); + } return; + + default: + discard(); + break; + } // switch +} + +quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e) +{ + switch (e->op) { + case IR::OpInvalid: + return V4Instr::Noop; + + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + return V4Instr::Noop; + + case IR::OpBitAnd: + return V4Instr::BitAndInt; + + case IR::OpBitOr: + return V4Instr::BitOrInt; + + case IR::OpBitXor: + return V4Instr::BitXorInt; + + case IR::OpAdd: + if (e->type == IR::StringType) + return V4Instr::AddString; + return V4Instr::AddReal; + + case IR::OpSub: + return V4Instr::SubReal; + + case IR::OpMul: + return V4Instr::MulReal; + + case IR::OpDiv: + return V4Instr::DivReal; + + case IR::OpMod: + return V4Instr::ModReal; + + case IR::OpLShift: + return V4Instr::LShiftInt; + + case IR::OpRShift: + return V4Instr::RShiftInt; + + case IR::OpURShift: + return V4Instr::URShiftInt; + + case IR::OpGt: + if (e->left->type == IR::StringType) + return V4Instr::GtString; + return V4Instr::GtReal; + + case IR::OpLt: + if (e->left->type == IR::StringType) + return V4Instr::LtString; + return V4Instr::LtReal; + + case IR::OpGe: + if (e->left->type == IR::StringType) + return V4Instr::GeString; + return V4Instr::GeReal; + + case IR::OpLe: + if (e->left->type == IR::StringType) + return V4Instr::LeString; + return V4Instr::LeReal; + + case IR::OpEqual: + if (e->left->type == IR::StringType) + return V4Instr::EqualString; + return V4Instr::EqualReal; + + case IR::OpNotEqual: + if (e->left->type == IR::StringType) + return V4Instr::NotEqualString; + return V4Instr::NotEqualReal; + + case IR::OpStrictEqual: + if (e->left->type == IR::StringType) + return V4Instr::StrictEqualString; + return V4Instr::StrictEqualReal; + + case IR::OpStrictNotEqual: + if (e->left->type == IR::StringType) + return V4Instr::StrictNotEqualString; + return V4Instr::StrictNotEqualReal; + + case IR::OpAnd: + case IR::OpOr: + return V4Instr::Noop; + + } // switch + + return V4Instr::Noop; +} + +void QV4CompilerPrivate::visitBinop(IR::Binop *e) +{ + int left = currentReg; + int right = currentReg + 1; + + if (e->left->asTemp() && e->type != IR::StringType) // Not sure if the e->type != String test is needed + left = e->left->asTemp()->index; + else + traceExpression(e->left, left); + + if (IR::Temp *t = e->right->asTemp()) + right = t->index; + else + traceExpression(e->right, right); + + if (e->left->type != e->right->type) { + if (qmlVerboseCompiler()) + qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op) + << "(`" << IR::binaryOperator(e->left->type) + << "' and `" + << IR::binaryOperator(e->right->type) + << "'"; + discard(); + return; + } + + switch (e->op) { + case IR::OpInvalid: + discard(); + break; + + // unary + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + discard(); + break; + + case IR::OpBitAnd: + case IR::OpBitOr: + case IR::OpBitXor: + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + convertToInt(e->left, left); + convertToInt(e->right, right); + break; + + case IR::OpAdd: + if (e->type != IR::StringType) { + convertToReal(e->left, left); + convertToReal(e->right, right); + } + break; + + case IR::OpSub: + case IR::OpMul: + case IR::OpDiv: + case IR::OpMod: + convertToReal(e->left, left); + convertToReal(e->right, right); + break; + + case IR::OpGt: + case IR::OpLt: + case IR::OpGe: + case IR::OpLe: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + if (e->left->type != IR::StringType) { + convertToReal(e->left, left); + convertToReal(e->right, right); + } + break; + + case IR::OpAnd: + case IR::OpOr: + discard(); // ### unreachable + break; + } // switch + + const quint8 opcode = instructionOpcode(e); + if (opcode != V4Instr::Noop) { + V4Instr instr; + instr.binaryop.output = currentReg; + instr.binaryop.left = left; + instr.binaryop.right = right; + gen(static_cast<V4Instr::Type>(opcode), instr); + } +} + +void QV4CompilerPrivate::visitCall(IR::Call *call) +{ + if (IR::Name *name = call->base->asName()) { + IR::Expr *arg = call->onlyArgument(); + if (arg != 0 && arg->type == IR::RealType) { + traceExpression(arg, currentReg); + + switch (name->builtin) { + case IR::NoBuiltinSymbol: + break; + + case IR::MathSinBultinFunction: { + Instr::MathSinReal i; + i.output = i.src = currentReg; + gen(i); + } return; + + case IR::MathCosBultinFunction: { + Instr::MathCosReal i; + i.output = i.src = currentReg; + gen(i); + } return; + + case IR::MathRoundBultinFunction: { + Instr::MathRoundReal i; + i.output = i.src = currentReg; + gen(i); + } return; + + case IR::MathFloorBultinFunction: { + Instr::MathFloorReal i; + i.output = i.src = currentReg; + gen(i); + } return; + + case IR::MathPIBuiltinConstant: + break; + } // switch + } + } + + if (qmlVerboseCompiler()) + qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__; + discard(); +} + + +// +// statements +// +void QV4CompilerPrivate::visitExp(IR::Exp *s) +{ + traceExpression(s->expr, currentReg); +} + +void QV4CompilerPrivate::visitMove(IR::Move *s) +{ + IR::Temp *target = s->target->asTemp(); + Q_ASSERT(target != 0); + + quint8 dest = target->index; + + if (target->type != s->source->type) { + quint8 src = dest; + + if (IR::Temp *t = s->source->asTemp()) + src = t->index; + else + traceExpression(s->source, dest); + + V4Instr::Type opcode = V4Instr::Noop; + IR::Type targetTy = s->target->type; + IR::Type sourceTy = s->source->type; + + if (sourceTy == IR::UrlType) { + switch (targetTy) { + case IR::BoolType: + case IR::StringType: + // nothing to do. V4 will generate optimized + // url-to-xxx conversions. + break; + default: { + // generate a UrlToString conversion and fix + // the type of the source expression. + V4Instr conv; + conv.unaryop.output = V4Instr::ConvertUrlToString; + conv.unaryop.src = src; + gen(opcode, conv); + + sourceTy = IR::StringType; + break; + } + } // switch + } + + if (targetTy == IR::BoolType) { + switch (sourceTy) { + case IR::IntType: opcode = V4Instr::ConvertIntToBool; break; + case IR::RealType: opcode = V4Instr::ConvertRealToBool; break; + case IR::StringType: opcode = V4Instr::ConvertStringToBool; break; + case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break; + default: break; + } // switch + } else if (targetTy == IR::IntType) { + switch (sourceTy) { + case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break; + case IR::RealType: { + if (s->isMoveForReturn) + opcode = V4Instr::MathRoundReal; + else + opcode = V4Instr::ConvertRealToInt; + break; + } + case IR::StringType: opcode = V4Instr::ConvertStringToInt; break; + default: break; + } // switch + } else if (targetTy == IR::RealType) { + switch (sourceTy) { + case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break; + case IR::IntType: opcode = V4Instr::ConvertIntToReal; break; + case IR::StringType: opcode = V4Instr::ConvertStringToReal; break; + default: break; + } // switch + } else if (targetTy == IR::StringType) { + switch (sourceTy) { + case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break; + case IR::IntType: opcode = V4Instr::ConvertIntToString; break; + case IR::RealType: opcode = V4Instr::ConvertRealToString; break; + case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break; + default: break; + } // switch + } else if (targetTy == IR::UrlType) { + V4Instr convToString; + convToString.unaryop.output = dest; + convToString.unaryop.src = src; + + // try to convert the source expression to a string. + switch (sourceTy) { + case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break; + case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break; + case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break; + default: break; + } // switch + + if (sourceTy == IR::StringType) + opcode = V4Instr::ConvertStringToUrl; + } + if (opcode != V4Instr::Noop) { + V4Instr conv; + conv.unaryop.output = dest; + conv.unaryop.src = src; + gen(opcode, conv); + + if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) { + V4Instr resolveUrl; + resolveUrl.unaryop.output = dest; + resolveUrl.unaryop.src = dest; + gen(V4Instr::ResolveUrl, resolveUrl); + } + } else { + discard(); + } + } else { + traceExpression(s->source, dest); + } +} + +void QV4CompilerPrivate::visitJump(IR::Jump *s) +{ + patches.append(Patch(s->target, bytecode.size())); + + Instr::Branch i; + i.offset = 0; // ### backpatch + gen(i); +} + +void QV4CompilerPrivate::visitCJump(IR::CJump *s) +{ + traceExpression(s->cond, currentReg); + + patches.append(Patch(s->iftrue, bytecode.size())); + + Instr::BranchTrue i; + i.reg = currentReg; + i.offset = 0; // ### backpatch + gen(i); +} + +void QV4CompilerPrivate::visitRet(IR::Ret *s) +{ + Q_ASSERT(s->expr != 0); + + int storeReg = currentReg; + + if (IR::Temp *temp = s->expr->asTemp()) { + storeReg = temp->index; + } else { + traceExpression(s->expr, storeReg); + } + + if (qmlBindingsTest) { + Instr::TestV4Store test; + test.reg = storeReg; + switch (s->type) { + case IR::StringType: + test.regType = QMetaType::QString; + break; + case IR::UrlType: + test.regType = QMetaType::QUrl; + break; + case IR::SGAnchorLineType: + test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId(); + break; + case IR::ObjectType: + test.regType = QMetaType::QObjectStar; + break; + case IR::BoolType: + test.regType = QMetaType::Bool; + break; + case IR::IntType: + test.regType = QMetaType::Int; + break; + case IR::RealType: + test.regType = QMetaType::QReal; + break; + default: + discard(); + return; + } + gen(test); + } + + Instr::Store store; + store.output = 0; + store.index = expression->property->index; + store.reg = storeReg; + store.exceptionId = exceptionId(s->line, s->column); + gen(store); +} + +void QV4Compiler::dump(const QByteArray &programData) +{ + const QV4Program *program = (const QV4Program *)programData.constData(); + + qWarning() << "Program.bindings:" << program->bindings; + qWarning() << "Program.dataLength:" << program->dataLength; + qWarning() << "Program.subscriptions:" << program->subscriptions; + qWarning() << "Program.indentifiers:" << program->identifiers; + + const int programSize = program->instructionCount; + const char *start = program->instructions(); + const char *end = start + programSize; + Bytecode bc; + bc.dump(start, end); +} + +/*! +Clear the state associated with attempting to compile a specific binding. +This does not clear the global "committed binding" states. +*/ +void QV4CompilerPrivate::resetInstanceState() +{ + data = committed.data; + exceptions = committed.exceptions; + usedSubscriptionIds.clear(); + subscriptionIds = committed.subscriptionIds; + registeredStrings = committed.registeredStrings; + bytecode.clear(); + patches.clear(); + pool.clear(); + currentReg = 0; +} + +/*! +Mark the last compile as successful, and add it to the "committed data" +section. + +Returns the index for the committed binding. +*/ +int QV4CompilerPrivate::commitCompile() +{ + int rv = committed.count(); + committed.offsets << committed.bytecode.count(); + committed.dependencies << usedSubscriptionIds; + committed.bytecode.append(bytecode.constData(), bytecode.size()); + committed.data = data; + committed.exceptions = exceptions; + committed.subscriptionIds = subscriptionIds; + committed.registeredStrings = registeredStrings; + return rv; +} + +bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node) +{ + resetInstanceState(); + + if (expression->property->type == -1) + return false; + + AST::SourceLocation location; + if (AST::ExpressionNode *astExpression = node->expressionCast()) { + location = astExpression->firstSourceLocation(); + } else if (AST::Statement *astStatement = node->statementCast()) { + if (AST::Block *block = AST::cast<AST::Block *>(astStatement)) + location = block->lbraceToken; + else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement)) + location = ifStmt->ifToken; + else + return false; + } else { + return false; + } + + IR::Function thisFunction(&pool), *function = &thisFunction; + + QV4IRBuilder irBuilder(expression, engine); + if (!irBuilder(function, node)) + return false; + + bool discarded = false; + qSwap(_discarded, discarded); + qSwap(_function, function); + trace(location.startLine, location.startColumn); + qSwap(_function, function); + qSwap(_discarded, discarded); + + if (qmlVerboseCompiler()) { + QTextStream qerr(stderr, QIODevice::WriteOnly); + if (discarded) + qerr << "======== TODO ====== " << endl; + else + qerr << "==================== " << endl; + qerr << "\tline: " << location.startLine + << "\tcolumn: " << location.startColumn + << endl; + foreach (IR::BasicBlock *bb, function->basicBlocks) + bb->dump(qerr); + qerr << endl; + } + + if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF) + return false; + + return true; +} + +// Returns a reg +int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str) +{ + // ### string cleanup + + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + Instr::LoadString string; + string.reg = reg; + string.offset = offset; + string.length = str.length(); + gen(string); + + return reg; +} + +// Returns an identifier offset +int QV4CompilerPrivate::registerString(const QString &string) +{ + Q_ASSERT(!string.isEmpty()); + + QPair<int, int> *iter = registeredStrings.value(string); + + if (!iter) { + quint32 len = string.length(); + QByteArray lendata((const char *)&len, sizeof(quint32)); + QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); + strdata.prepend(lendata); + int rv = data.count(); + data += strdata; + + iter = ®isteredStrings[string]; + *iter = qMakePair(registeredStrings.count(), rv); + } + + Instr::InitString reg; + reg.offset = iter->first; + reg.dataIdx = iter->second; + gen(reg); + return reg.offset; +} + +/*! +Returns true if the current expression has not already subscribed to \a sub in currentBlockMask. +*/ +bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + + int *iter = subscriptionIds.value(str); + if (!iter) + return true; + + quint32 *uiter = usedSubscriptionIds.value(*iter); + if (!uiter) + return true; + else + return !(*uiter & currentBlockMask); +} + +int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + int *iter = subscriptionIds.value(str); + if (!iter) { + int count = subscriptionIds.count(); + iter = &subscriptionIds[str]; + *iter = count; + } + quint32 &u = usedSubscriptionIds[*iter]; + if (!(u & currentBlockMask)) { + u |= currentBlockMask; + usedSubscriptionIdsChanged = true; + } + return *iter; +} + +quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + + int *iter = subscriptionIds.value(str); + Q_ASSERT(iter != 0); + + quint32 *uiter = usedSubscriptionIds.value(*iter); + Q_ASSERT(uiter != 0); + + return *uiter; +} + +quint8 QV4CompilerPrivate::exceptionId(quint32 line, quint32 column) +{ + quint8 rv = 0xFF; + if (exceptions.count() < 0xFF) { + rv = (quint8)exceptions.count(); + quint64 e = line; + e <<= 32; + e |= column; + exceptions.append(e); + } + return rv; +} + +quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n) +{ + quint8 rv = 0xFF; + if (n && exceptions.count() < 0xFF) { + QQmlJS::AST::SourceLocation l = n->firstSourceLocation(); + rv = exceptionId(l.startLine, l.startColumn); + } + return rv; +} + +QV4Compiler::QV4Compiler() +: d(new QV4CompilerPrivate) +{ + qmlBindingsTest |= qmlBindingsTestEnv(); +} + +QV4Compiler::~QV4Compiler() +{ + delete d; d = 0; +} + +/* +Returns true if any bindings were compiled. +*/ +bool QV4Compiler::isValid() const +{ + return !d->committed.bytecode.isEmpty(); +} + +/* +-1 on failure, otherwise the binding index to use. +*/ +int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine) +{ + if (!expression.expression.asAST()) return false; + + if (!qmlExperimental() && expression.property->isValueTypeSubProperty) + return -1; + + if (qmlDisableOptimizer() || !qmlEnableV4) + return -1; + + d->expression = &expression; + d->engine = engine; + + if (d->compile(expression.expression.asAST())) { + return d->commitCompile(); + } else { + return -1; + } +} + +QByteArray QV4CompilerPrivate::buildSignalTable() const +{ + QHash<int, QList<QPair<int, quint32> > > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii); + for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter) + table[iter->first].append(qMakePair(ii, iter->second)); + } + + QVector<quint32> header; + QVector<quint32> data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList<QPair<int, quint32> > &bindings = table[ii]; + data.append(bindings.count()); + for (int jj = 0; jj < bindings.count(); ++jj) { + data.append(bindings.at(jj).first); + data.append(bindings.at(jj).second); + } + } + header << data; + + return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); +} + +QByteArray QV4CompilerPrivate::buildExceptionData() const +{ + QByteArray rv; + rv.resize(committed.exceptions.count() * sizeof(quint64)); + ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); + return rv; +} + +/* +Returns the compiled program. +*/ +QByteArray QV4Compiler::program() const +{ + QByteArray programData; + + if (isValid()) { + QV4Program prog; + prog.bindings = d->committed.count(); + + Bytecode bc; + QV4CompilerPrivate::Instr::Jump jump; + jump.reg = -1; + + for (int ii = 0; ii < d->committed.count(); ++ii) { + jump.count = d->committed.count() - ii - 1; + jump.count*= V4InstrMeta<V4Instr::Jump>::Size; + jump.count+= d->committed.offsets.at(ii); + bc.append(jump); + } + + + QByteArray bytecode; + bytecode.reserve(bc.size() + d->committed.bytecode.size()); + bytecode.append(bc.constData(), bc.size()); + bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size()); + + QByteArray data = d->committed.data; + while (data.count() % 4) data.append('\0'); + prog.signalTableOffset = data.count(); + data += d->buildSignalTable(); + while (data.count() % 4) data.append('\0'); + prog.exceptionDataOffset = data.count(); + data += d->buildExceptionData(); + + prog.dataLength = 4 * ((data.size() + 3) / 4); + prog.subscriptions = d->committed.subscriptionIds.count(); + prog.identifiers = d->committed.registeredStrings.count(); + prog.instructionCount = bytecode.count(); + int size = sizeof(QV4Program) + bytecode.count(); + size += prog.dataLength; + + programData.resize(size); + memcpy(programData.data(), &prog, sizeof(QV4Program)); + if (prog.dataLength) + memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(), + data.size()); + memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(), + bytecode.count()); + } + + if (bindingsDump()) { + qWarning().nospace() << "Subscription slots:"; + + for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin(); + iter != d->committed.subscriptionIds.end(); + ++iter) { + qWarning().nospace() << " " << iter->first << "\t-> " << iter->second; + } + + QV4Compiler::dump(programData); + } + + return programData; +} + +void QV4Compiler::enableBindingsTest(bool e) +{ + if (e) + qmlBindingsTest = true; + else + qmlBindingsTest = qmlBindingsTestEnv(); +} + +void QV4Compiler::enableV4(bool e) +{ + qmlEnableV4 = e; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4compiler_p.h new file mode 100644 index 0000000000..a93248ad14 --- /dev/null +++ b/src/qml/qml/v4/qv4compiler_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4COMPILER_P_H +#define QV4COMPILER_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/qqmlexpression_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qqmlcompiler_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQmlTypeNameCache; +class QV4CompilerPrivate; +class Q_AUTOTEST_EXPORT QV4Compiler +{ +public: + QV4Compiler(); + ~QV4Compiler(); + + // Returns true if bindings were compiled + bool isValid() const; + + struct Expression + { + Expression(const QQmlImports &imp) : imports(imp) {} + QQmlScript::Object *component; + QQmlScript::Object *context; + QQmlScript::Property *property; + QQmlScript::Variant expression; + QQmlCompilerTypes::IdList *ids; + QQmlTypeNameCache *importCache; + QQmlImports imports; + }; + + // -1 on failure, otherwise the binding index to use + int compile(const Expression &, QQmlEnginePrivate *); + + // Returns the compiled program + QByteArray program() const; + + static void dump(const QByteArray &); + static void enableBindingsTest(bool); + static void enableV4(bool); +private: + QV4CompilerPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4COMPILER_P_H + diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h new file mode 100644 index 0000000000..4b74a1d1c5 --- /dev/null +++ b/src/qml/qml/v4/qv4compiler_p_p.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4COMPILER_P_P_H +#define QV4COMPILER_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 "qv4instruction_p.h" +#include "qv4ir_p.h" +#include <private/qqmlscript_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +template <typename _Key, typename _Value> +class QQmlAssociationList +{ +public: + typedef QVarLengthArray<QPair<_Key, _Value>, 8> Container; + typedef typename Container::const_iterator const_iterator; + typedef typename Container::const_iterator ConstIterator; + + const_iterator begin() const { return _container.begin(); } + const_iterator end() const { return _container.end(); } + int count() const { return _container.count(); } + void clear() { _container.clear(); } + + _Value *value(const _Key &key) { + for (int i = 0; i < _container.size(); ++i) { + QPair<_Key, _Value> &p = _container[i]; + if (p.first == key) + return &p.second; + } + return 0; + } + + _Value &operator[](const _Key &key) { + for (int i = 0; i < _container.size(); ++i) { + QPair<_Key, _Value> &p = _container[i]; + if (p.first == key) + return p.second; + } + int index = _container.size(); + _container.append(qMakePair(key, _Value())); + return _container[index].second; + } + + void insert(const _Key &key, _Value &value) { + for (int i = 0; i < _container.size(); ++i) { + QPair<_Key, _Value> &p = _container[i]; + if (p.first == key) { + p.second = value; + return; + } + } + _container.append(qMakePair(key, value)); + } + +private: + Container _container; +}; + +class QV4CompilerPrivate: protected QQmlJS::IR::ExprVisitor, + protected QQmlJS::IR::StmtVisitor +{ +public: + QV4CompilerPrivate(); + + void resetInstanceState(); + int commitCompile(); + + const QV4Compiler::Expression *expression; + QQmlEnginePrivate *engine; + + QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)expression->context, 16); } + + bool compile(QQmlJS::AST::Node *); + + int registerLiteralString(quint8 reg, const QStringRef &); + int registerString(const QString &); + QQmlAssociationList<QString, QPair<int, int> > registeredStrings; + QByteArray data; + + bool blockNeedsSubscription(const QStringList &); + int subscriptionIndex(const QStringList &); + quint32 subscriptionBlockMask(const QStringList &); + + quint8 exceptionId(quint32 line, quint32 column); + quint8 exceptionId(QQmlJS::AST::ExpressionNode *); + QVector<quint64> exceptions; + + QQmlAssociationList<int, quint32> usedSubscriptionIds; + + QQmlAssociationList<QString, int> subscriptionIds; + QQmlJS::Bytecode bytecode; + + // back patching + struct Patch { + QQmlJS::IR::BasicBlock *block; // the basic block + int offset; // the index of the instruction to patch + Patch(QQmlJS::IR::BasicBlock *block = 0, int index = -1) + : block(block), offset(index) {} + }; + QVector<Patch> patches; + QQmlPool pool; + + // Committed binding data + struct { + QList<int> offsets; + QList<QQmlAssociationList<int, quint32> > dependencies; + + //QQmlJS::Bytecode bytecode; + QByteArray bytecode; + QByteArray data; + QQmlAssociationList<QString, int> subscriptionIds; + QVector<quint64> exceptions; + + QQmlAssociationList<QString, QPair<int, int> > registeredStrings; + + int count() const { return offsets.count(); } + } committed; + + QByteArray buildSignalTable() const; + QByteArray buildExceptionData() const; + + void convertToReal(QQmlJS::IR::Expr *expr, int reg); + void convertToInt(QQmlJS::IR::Expr *expr, int reg); + void convertToBool(QQmlJS::IR::Expr *expr, int reg); + quint8 instructionOpcode(QQmlJS::IR::Binop *e); + + struct Instr { +#define QML_V4_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlJS::V4InstrData<QQmlJS::V4Instr::I> I; + FOR_EACH_V4_INSTR(QML_V4_INSTR_DATA_TYPEDEF) +#undef QML_v4_INSTR_DATA_TYPEDEF + private: + Instr(); + }; + +protected: + // + // tracing + // + void trace(int line, int column); + void trace(QVector<QQmlJS::IR::BasicBlock *> *blocks); + void traceExpression(QQmlJS::IR::Expr *e, quint8 r); + + template <int Instr> + inline void gen(const QQmlJS::V4InstrData<Instr> &i) + { bytecode.append(i); } + inline void gen(QQmlJS::V4Instr::Type type, QQmlJS::V4Instr &instr) + { bytecode.append(type, instr); } + + inline QQmlJS::V4Instr::Type instructionType(const QQmlJS::V4Instr *i) const + { return bytecode.instructionType(i); } + + // + // expressions + // + virtual void visitConst(QQmlJS::IR::Const *e); + virtual void visitString(QQmlJS::IR::String *e); + virtual void visitName(QQmlJS::IR::Name *e); + virtual void visitTemp(QQmlJS::IR::Temp *e); + virtual void visitUnop(QQmlJS::IR::Unop *e); + virtual void visitBinop(QQmlJS::IR::Binop *e); + virtual void visitCall(QQmlJS::IR::Call *e); + + // + // statements + // + virtual void visitExp(QQmlJS::IR::Exp *s); + virtual void visitMove(QQmlJS::IR::Move *s); + virtual void visitJump(QQmlJS::IR::Jump *s); + virtual void visitCJump(QQmlJS::IR::CJump *s); + virtual void visitRet(QQmlJS::IR::Ret *s); + +private: + QStringList _subscribeName; + QQmlJS::IR::Function *_function; + QQmlJS::IR::BasicBlock *_block; + void discard() { _discarded = true; } + bool _discarded; + quint8 currentReg; + + bool usedSubscriptionIdsChanged; + quint32 currentBlockMask; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4COMPILER_P_P_H + diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp new file mode 100644 index 0000000000..08b2570747 --- /dev/null +++ b/src/qml/qml/v4/qv4instruction.cpp @@ -0,0 +1,412 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4instruction_p.h" +#include "qv4bindings_p.h" + +#include <QtCore/qdebug.h> +#include <private/qqmlglobal_p.h> + +// Define this to do a test dump of all the instructions at startup. This is +// helpful to test that each instruction's Instr::dump() case uses the correct +// number of tabs etc and otherwise looks correct. +// #define DEBUG_INSTR_DUMP + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +#ifdef DEBUG_INSTR_DUMP +static struct DumpInstrAtStartup { + DumpInstrAtStartup() { + Bytecode bc; +#define DUMP_INSTR_AT_STARTUP(I, FMT) { V4InstrData<V4Instr::I> i; bc.append(i); } + FOR_EACH_V4_INSTR(DUMP_INSTR_AT_STARTUP); +#undef DUMP_INSTR_AT_STARTUP + const char *start = bc.constData(); + const char *end = start + bc.size(); + bc.dump(start, end); + } +} dump_instr_at_startup; +#endif + +int V4Instr::size(Type type) +{ +#define V4_RETURN_INSTR_SIZE(I, FMT) case I: return QML_V4_INSTR_SIZE(I, FMT); + switch (type) { + FOR_EACH_V4_INSTR(V4_RETURN_INSTR_SIZE) + } +#undef V4_RETURN_INSTR_SIZE + return 0; +} + +void Bytecode::dump(const V4Instr *i, int address) const +{ + QByteArray leading; + if (address != -1) { + leading = QByteArray::number(address); + leading.prepend(QByteArray(8 - leading.count(), ' ')); + leading.append("\t"); + } + +#define INSTR_DUMP qWarning().nospace() << leading.constData() + + switch (instructionType(i)) { + case V4Instr::Noop: + INSTR_DUMP << "\t" << "Noop"; + break; + case V4Instr::BindingId: + INSTR_DUMP << i->id.line << ":" << i->id.column << ":"; + break; + case V4Instr::Subscribe: + INSTR_DUMP << "\t" << "Subscribe" << "\t\t" << "Object_Reg(" << i->subscribeop.reg << ") Notify_Signal(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ")"; + break; + case V4Instr::SubscribeId: + INSTR_DUMP << "\t" << "SubscribeId" << "\t\t" << "Id_Offset(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ")"; + break; + case V4Instr::FetchAndSubscribe: + INSTR_DUMP << "\t" << "FetchAndSubscribe" << "\t" << "Object_Reg(" << i->fetchAndSubscribe.reg << ") Fast_Accessor(" << i->fetchAndSubscribe.property.accessors << ") -> Output_Reg(" << i->fetchAndSubscribe.reg << ") Subscription_Slot(" << i->fetchAndSubscribe.subscription << ")"; + break; + case V4Instr::LoadId: + INSTR_DUMP << "\t" << "LoadId" << "\t\t\t" << "Id_Offset(" << i->load.index << ") -> Output_Reg(" << i->load.reg << ")"; + break; + case V4Instr::LoadScope: + INSTR_DUMP << "\t" << "LoadScope" << "\t\t" << "-> Output_Reg(" << i->load.reg << ")"; + break; + case V4Instr::LoadRoot: + INSTR_DUMP << "\t" << "LoadRoot" << "\t\t" << "-> Output_Reg(" << i->load.reg << ")"; + break; + case V4Instr::LoadAttached: + INSTR_DUMP << "\t" << "LoadAttached" << "\t\t" << "Object_Reg(" << i->attached.reg << ") Attached_Index(" << i->attached.id << ") -> Output_Reg(" << i->attached.output << ")"; + break; + case V4Instr::UnaryNot: + INSTR_DUMP << "\t" << "UnaryNot" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::UnaryMinusReal: + INSTR_DUMP << "\t" << "UnaryMinusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::UnaryMinusInt: + INSTR_DUMP << "\t" << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::UnaryPlusReal: + INSTR_DUMP << "\t" << "UnaryPlusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::UnaryPlusInt: + INSTR_DUMP << "\t" << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertBoolToInt: + INSTR_DUMP << "\t" << "ConvertBoolToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertBoolToReal: + INSTR_DUMP << "\t" << "ConvertBoolToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertBoolToString: + INSTR_DUMP << "\t" << "ConvertBoolToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertIntToBool: + INSTR_DUMP << "\t" << "ConvertIntToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertIntToReal: + INSTR_DUMP << "\t" << "ConvertIntToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertIntToString: + INSTR_DUMP << "\t" << "ConvertIntToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertRealToBool: + INSTR_DUMP << "\t" << "ConvertRealToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertRealToInt: + INSTR_DUMP << "\t" << "ConvertRealToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertRealToString: + INSTR_DUMP << "\t" << "ConvertRealToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertStringToBool: + INSTR_DUMP << "\t" << "ConvertStringToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertStringToInt: + INSTR_DUMP << "\t" << "ConvertStringToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertStringToReal: + INSTR_DUMP << "\t" << "ConvertStringToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertStringToUrl: + INSTR_DUMP << "\t" << "ConvertStringToUrl" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertUrlToBool: + INSTR_DUMP << "\t" << "ConvertUrlToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertUrlToString: + INSTR_DUMP << "\t" << "ConvertUrlToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ResolveUrl: + INSTR_DUMP << "\t" << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::MathSinReal: + INSTR_DUMP << "\t" << "MathSinReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::MathCosReal: + INSTR_DUMP << "\t" << "MathCosReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::MathRoundReal: + INSTR_DUMP << "\t" << "MathRoundReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::MathFloorReal: + INSTR_DUMP << "\t" << "MathFloorReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::MathPIReal: + INSTR_DUMP << "\t" << "MathPIReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::LoadReal: + INSTR_DUMP << "\t" << "LoadReal" << "\t\t" << "Constant(" << i->real_value.value << ") -> Output_Reg(" << i->real_value.reg << ")"; + break; + case V4Instr::LoadInt: + INSTR_DUMP << "\t" << "LoadInt" << "\t\t\t" << "Constant(" << i->int_value.value << ") -> Output_Reg(" << i->int_value.reg << ")"; + break; + case V4Instr::LoadBool: + INSTR_DUMP << "\t" << "LoadBool" << "\t\t" << "Constant(" << i->bool_value.value << ") -> Output_Reg(" << i->bool_value.reg << ")"; + break; + case V4Instr::LoadString: + INSTR_DUMP << "\t" << "LoadString" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ") -> Output_Register(" << i->string_value.reg << ")"; + break; + case V4Instr::EnableV4Test: + INSTR_DUMP << "\t" << "EnableV4Test" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ")"; + break; + case V4Instr::TestV4Store: + INSTR_DUMP << "\t" << "TestV4Store" << "\t\t" << "Input_Reg(" << i->storetest.reg << ") Reg_Type(" << i->storetest.regType << ")"; + break; + case V4Instr::BitAndInt: + INSTR_DUMP << "\t" << "BitAndInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::BitOrInt: + INSTR_DUMP << "\t" << "BitOrInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::BitXorInt: + INSTR_DUMP << "\t" << "BitXorInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::AddReal: + INSTR_DUMP << "\t" << "AddReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::AddString: + INSTR_DUMP << "\t" << "AddString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::SubReal: + INSTR_DUMP << "\t" << "SubReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::MulReal: + INSTR_DUMP << "\t" << "MulReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::DivReal: + INSTR_DUMP << "\t" << "DivReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::ModReal: + INSTR_DUMP << "\t" << "ModReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::LShiftInt: + INSTR_DUMP << "\t" << "LShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::RShiftInt: + INSTR_DUMP << "\t" << "RShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::URShiftInt: + INSTR_DUMP << "\t" << "URShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::GtReal: + INSTR_DUMP << "\t" << "GtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::LtReal: + INSTR_DUMP << "\t" << "LtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::GeReal: + INSTR_DUMP << "\t" << "GeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::LeReal: + INSTR_DUMP << "\t" << "LeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::EqualReal: + INSTR_DUMP << "\t" << "EqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::NotEqualReal: + INSTR_DUMP << "\t" << "NotEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::StrictEqualReal: + INSTR_DUMP << "\t" << "StrictEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::StrictNotEqualReal: + INSTR_DUMP << "\t" << "StrictNotEqualReal" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::GtString: + INSTR_DUMP << "\t" << "GtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::LtString: + INSTR_DUMP << "\t" << "LtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::GeString: + INSTR_DUMP << "\t" << "GeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::LeString: + INSTR_DUMP << "\t" << "LeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::EqualString: + INSTR_DUMP << "\t" << "EqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::NotEqualString: + INSTR_DUMP << "\t" << "NotEqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::StrictEqualString: + INSTR_DUMP << "\t" << "StrictEqualString" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::StrictNotEqualString: + INSTR_DUMP << "\t" << "StrictNotEqualString" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + break; + case V4Instr::NewString: + INSTR_DUMP << "\t" << "NewString" << "\t\t" << "Register(" << i->construct.reg << ")"; + break; + case V4Instr::NewUrl: + INSTR_DUMP << "\t" << "NewUrl" << "\t\t\t" << "Register(" << i->construct.reg << ")"; + break; + case V4Instr::CleanupRegister: + INSTR_DUMP << "\t" << "CleanupRegister" << "\t\t" << "Register(" << i->cleanup.reg << ")"; + break; + case V4Instr::Fetch: + INSTR_DUMP << "\t" << "Fetch" << "\t\t\t" << "Object_Reg(" << i->fetch.reg << ") Property_Index(" << i->fetch.index << ") -> Output_Reg(" << i->fetch.reg << ")"; + break; + case V4Instr::Store: + INSTR_DUMP << "\t" << "Store" << "\t\t\t" << "Input_Reg(" << i->store.reg << ") -> Object_Reg(" << i->store.output << ") Property_Index(" << i->store.index << ")"; + break; + case V4Instr::Copy: + INSTR_DUMP << "\t" << "Copy" << "\t\t\t" << "Input_Reg(" << i->copy.src << ") -> Output_Reg(" << i->copy.reg << ")"; + break; + case V4Instr::Jump: + if (i->jump.reg != -1) { + INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ") [if false == Input_Reg(" << i->jump.reg << ")]"; + } else { + INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ")"; + } + break; + case V4Instr::BranchFalse: + INSTR_DUMP << "\t" << "BranchFalse" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if false == Input_Reg(" << i->branchop.reg << ")]"; + break; + case V4Instr::BranchTrue: + INSTR_DUMP << "\t" << "BranchTrue" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if true == Input_Reg(" << i->branchop.reg << ")]"; + break; + case V4Instr::Branch: + INSTR_DUMP << "\t" << "Branch" << "\t\t\t" << "Address(" << (address + size() + i->branchop.offset) << ")"; + break; + case V4Instr::InitString: + INSTR_DUMP << "\t" << "InitString" << "\t\t" << "String_DataIndex(" << i->initstring.dataIdx << ") -> String_Slot(" << i->initstring.offset << ")"; + break; + case V4Instr::Block: + INSTR_DUMP << "\t" << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(i->blockop.block, 16).constData() << ")"; + break; + default: + INSTR_DUMP << "\t" << "Unknown"; + break; + } +} + +void Bytecode::dump(const char *start, const char *end) const +{ + const char *code = start; + while (code < end) { + const V4Instr *instr = reinterpret_cast<const V4Instr *>(code); + dump(instr, code - start); + code += V4Instr::size(instructionType(instr)); + } +} + +Bytecode::Bytecode() +{ +#ifdef QML_THREADED_INTERPRETER + decodeInstr = QV4Bindings::getDecodeInstrTable(); +#endif +} + +V4Instr::Type Bytecode::instructionType(const V4Instr *instr) const +{ +#ifdef QML_THREADED_INTERPRETER + void *code = instr->common.code; + +# define CHECK_V4_INSTR_CODE(I, FMT) \ + if (decodeInstr[static_cast<int>(V4Instr::I)] == code) \ + return V4Instr::I; + + FOR_EACH_V4_INSTR(CHECK_V4_INSTR_CODE) + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid instruction address"); + return static_cast<V4Instr::Type>(0); +# undef CHECK_V4_INSTR_CODE +#else + return static_cast<V4Instr::Type>(instr->common.type); +#endif + +} + +void Bytecode::append(V4Instr::Type type, V4Instr &instr) +{ +#ifdef QML_THREADED_INTERPRETER + instr.common.code = decodeInstr[static_cast<int>(type)]; +#else + instr.common.type = type; +#endif + d.append(reinterpret_cast<const char *>(&instr), V4Instr::size(type)); +} + +int Bytecode::remove(int offset) +{ + const V4Instr *instr = reinterpret_cast<const V4Instr *>(d.begin() + offset); + const int instrSize = V4Instr::size(instructionType(instr)); + d.remove(offset, instrSize); + return instrSize; +} + +const V4Instr &Bytecode::operator[](int offset) const +{ + return *(reinterpret_cast<const V4Instr *>(d.begin() + offset)); +} + +V4Instr &Bytecode::operator[](int offset) +{ + return *(reinterpret_cast<V4Instr *>(d.begin() + offset)); +} + +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h new file mode 100644 index 0000000000..8150eedf54 --- /dev/null +++ b/src/qml/qml/v4/qv4instruction_p.h @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4INSTRUCTION_P_H +#define QV4INSTRUCTION_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/qbytearray.h> +#include <QtCore/qvector.h> +#include <QtCore/qvarlengtharray.h> + +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_V4_INSTR(F) \ + F(Noop, common) \ + F(BindingId, id) \ + F(Subscribe, subscribeop) \ + F(SubscribeId, subscribeop) \ + F(FetchAndSubscribe, fetchAndSubscribe) \ + F(LoadId, load) \ + F(LoadScope, load) \ + F(LoadRoot, load) \ + F(LoadAttached, attached) \ + F(UnaryNot, unaryop) \ + F(UnaryMinusReal, unaryop) \ + F(UnaryMinusInt, unaryop) \ + F(UnaryPlusReal, unaryop) \ + F(UnaryPlusInt, unaryop) \ + F(ConvertBoolToInt, unaryop) \ + F(ConvertBoolToReal, unaryop) \ + F(ConvertBoolToString, unaryop) \ + F(ConvertIntToBool, unaryop) \ + F(ConvertIntToReal, unaryop) \ + F(ConvertIntToString, unaryop) \ + F(ConvertRealToBool, unaryop) \ + F(ConvertRealToInt, unaryop) \ + F(ConvertRealToString, unaryop) \ + F(ConvertStringToBool, unaryop) \ + F(ConvertStringToInt, unaryop) \ + F(ConvertStringToReal, unaryop) \ + F(ConvertStringToUrl, unaryop) \ + F(ConvertUrlToBool, unaryop) \ + F(ConvertUrlToString, unaryop) \ + F(ResolveUrl, unaryop) \ + F(MathSinReal, unaryop) \ + F(MathCosReal, unaryop) \ + F(MathRoundReal, unaryop) \ + F(MathFloorReal, unaryop) \ + F(MathPIReal, unaryop) \ + F(LoadReal, real_value) \ + F(LoadInt, int_value) \ + F(LoadBool, bool_value) \ + F(LoadString, string_value) \ + F(EnableV4Test, string_value) \ + F(TestV4Store, storetest) \ + F(BitAndInt, binaryop) \ + F(BitOrInt, binaryop) \ + F(BitXorInt, binaryop) \ + F(AddReal, binaryop) \ + F(AddString, binaryop) \ + F(SubReal, binaryop) \ + F(MulReal, binaryop) \ + F(DivReal, binaryop) \ + F(ModReal, binaryop) \ + F(LShiftInt, binaryop) \ + F(RShiftInt, binaryop) \ + F(URShiftInt, binaryop) \ + F(GtReal, binaryop) \ + F(LtReal, binaryop) \ + F(GeReal, binaryop) \ + F(LeReal, binaryop) \ + F(EqualReal, binaryop) \ + F(NotEqualReal, binaryop) \ + F(StrictEqualReal, binaryop) \ + F(StrictNotEqualReal, binaryop) \ + F(GtString, binaryop) \ + F(LtString, binaryop) \ + F(GeString, binaryop) \ + F(LeString, binaryop) \ + F(EqualString, binaryop) \ + F(NotEqualString, binaryop) \ + F(StrictEqualString, binaryop) \ + F(StrictNotEqualString, binaryop) \ + F(NewString, construct) \ + F(NewUrl, construct) \ + F(CleanupRegister, cleanup) \ + F(Copy, copy) \ + F(Fetch, fetch) \ + F(Store, store) \ + F(Jump, jump) \ + F(BranchTrue, branchop) \ + F(BranchFalse, branchop) \ + F(Branch, branchop) \ + F(Block, blockop) \ + /* Speculative property resolution */ \ + F(InitString, initstring) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define QML_THREADED_INTERPRETER +#endif + +#ifdef Q_ALIGNOF +# define QML_V4_INSTR_ALIGN_MASK (Q_ALIGNOF(V4Instr) - 1) +#else +# define QML_V4_INSTR_ALIGN_MASK (sizeof(void *) - 1) +#endif + +#define QML_V4_INSTR_ENUM(I, FMT) I, +#define QML_V4_INSTR_ADDR(I, FMT) &&op_##I, +#define QML_V4_INSTR_SIZE(I, FMT) ((sizeof(V4Instr::instr_##FMT) + QML_V4_INSTR_ALIGN_MASK) & ~QML_V4_INSTR_ALIGN_MASK) + +#ifdef QML_THREADED_INTERPRETER +# define QML_V4_BEGIN_INSTR(I,FMT) op_##I: +# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; goto *instr->common.code; +# define QML_V4_INSTR_HEADER void *code; +#else +# define QML_V4_BEGIN_INSTR(I,FMT) case V4Instr::I: +# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; break; +# define QML_V4_INSTR_HEADER quint8 type; +#endif + +class QObject; +class QQmlNotifier; + +namespace QQmlJS { + +union V4Instr { + enum Type { + FOR_EACH_V4_INSTR(QML_V4_INSTR_ENUM) + }; + + static int size(Type type); + + struct instr_common { + QML_V4_INSTR_HEADER + }; + + struct instr_id { + QML_V4_INSTR_HEADER + quint16 column; + quint32 line; + }; + + struct instr_init { + QML_V4_INSTR_HEADER + quint16 subscriptions; + quint16 identifiers; + }; + + struct instr_subscribeop { + QML_V4_INSTR_HEADER + qint8 reg; + quint16 offset; + quint32 index; + }; + + struct instr_load { + QML_V4_INSTR_HEADER + qint8 reg; + quint32 index; + }; + + struct instr_attached { + QML_V4_INSTR_HEADER + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 id; + }; + + struct instr_store { + QML_V4_INSTR_HEADER + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + }; + + struct instr_storetest { + QML_V4_INSTR_HEADER + qint8 reg; + qint32 regType; + }; + + struct instr_fetchAndSubscribe { + QML_V4_INSTR_HEADER + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint16 subscription; + QQmlPropertyRawData property; + }; + + struct instr_fetch{ + QML_V4_INSTR_HEADER + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint32 index; + }; + + struct instr_copy { + QML_V4_INSTR_HEADER + qint8 reg; + qint8 src; + }; + + struct instr_construct { + QML_V4_INSTR_HEADER + qint8 reg; + }; + + struct instr_real_value { + QML_V4_INSTR_HEADER + qint8 reg; + qreal value; // XXX Makes the instruction 12 bytes + }; + + struct instr_int_value { + QML_V4_INSTR_HEADER + qint8 reg; + int value; + }; + + struct instr_bool_value { + QML_V4_INSTR_HEADER + qint8 reg; + bool value; + }; + + struct instr_string_value { + QML_V4_INSTR_HEADER + qint8 reg; + quint16 length; + quint32 offset; + }; + + struct instr_binaryop { + QML_V4_INSTR_HEADER + qint8 output; + qint8 left; + qint8 right; + }; + + struct instr_unaryop { + QML_V4_INSTR_HEADER + qint8 output; + qint8 src; + }; + + struct instr_jump { + QML_V4_INSTR_HEADER + qint8 reg; + quint32 count; + }; + + struct instr_find { + QML_V4_INSTR_HEADER + qint8 reg; + qint8 src; + quint8 exceptionId; + quint16 name; + quint16 subscribeIndex; + }; + + struct instr_cleanup { + QML_V4_INSTR_HEADER + qint8 reg; + }; + + struct instr_initstring { + QML_V4_INSTR_HEADER + quint16 offset; + quint32 dataIdx; + }; + + struct instr_branchop { + QML_V4_INSTR_HEADER + quint8 reg; + qint16 offset; + }; + + struct instr_blockop { + QML_V4_INSTR_HEADER + quint32 block; + }; + + instr_common common; + instr_id id; + instr_init init; + instr_subscribeop subscribeop; + instr_load load; + instr_attached attached; + instr_store store; + instr_storetest storetest; + instr_fetchAndSubscribe fetchAndSubscribe; + instr_fetch fetch; + instr_copy copy; + instr_construct construct; + instr_real_value real_value; + instr_int_value int_value; + instr_bool_value bool_value; + instr_string_value string_value; + instr_binaryop binaryop; + instr_unaryop unaryop; + instr_jump jump; + instr_find find; + instr_cleanup cleanup; + instr_initstring initstring; + instr_branchop branchop; + instr_blockop blockop; +}; + +template<int N> +struct V4InstrMeta { +}; + +#define QML_V4_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct V4InstrMeta<(int)V4Instr::I> { \ + enum { Size = QML_V4_INSTR_SIZE(I, FMT) }; \ + typedef V4Instr::instr_##FMT DataType; \ + static const DataType &data(const V4Instr &instr) { return instr.FMT; } \ + static void setData(V4Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_V4_INSTR(QML_V4_INSTR_META_TEMPLATE); +#undef QML_V4_INSTR_META_TEMPLATE + +template<int Instr> +class V4InstrData : public V4InstrMeta<Instr>::DataType +{ +}; + +class Bytecode +{ + Q_DISABLE_COPY(Bytecode) + +public: + Bytecode(); + + const char *constData() const { return d.constData(); } + int size() const { return d.size(); } + int count() const { return d.count(); } + void clear() { d.clear(); } + bool isEmpty() const { return d.isEmpty(); } + V4Instr::Type instructionType(const V4Instr *instr) const; + + template <int Instr> + void append(const V4InstrData<Instr> &data) + { + V4Instr genericInstr; + V4InstrMeta<Instr>::setData(genericInstr, data); + return append(static_cast<V4Instr::Type>(Instr), genericInstr); + } + void append(V4Instr::Type type, V4Instr &instr); + + int remove(int index); + + const V4Instr &operator[](int offset) const; + V4Instr &operator[](int offset); + + void dump(const char *start, const char *end) const; + +private: + void dump(const V4Instr *instr, int = -1) const; + + QVarLengthArray<char, 4 * 1024> d; +#ifdef QML_THREADED_INTERPRETER + void **decodeInstr; +#endif +}; + +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4INSTRUCTION_P_H + diff --git a/src/qml/qml/v4/qv4ir.cpp b/src/qml/qml/v4/qv4ir.cpp new file mode 100644 index 0000000000..be822145a4 --- /dev/null +++ b/src/qml/qml/v4/qv4ir.cpp @@ -0,0 +1,882 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ir_p.h" + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace IR { + +inline const char *typeName(Type t) +{ + switch (t) { + case InvalidType: return "invalid"; + case UndefinedType: return "undefined"; + case NullType: return "null"; + case VoidType: return "void"; + case StringType: return "string"; + case UrlType: return "url"; + case SGAnchorLineType: return "SGAnchorLine"; + case AttachType: return "AttachType"; + case ObjectType: return "object"; + case BoolType: return "bool"; + case IntType: return "int"; + case RealType: return "qreal"; + case RealNaNType: return "NaN"; + default: return "invalid"; + } +} + +inline bool isNumberType(IR::Type ty) +{ + return ty >= IR::FirstNumberType; +} + +inline bool isStringType(IR::Type ty) +{ + return ty == IR::StringType || ty == IR::UrlType; +} + +IR::Type maxType(IR::Type left, IR::Type right) +{ + if (isStringType(left) && isStringType(right)) { + // String promotions (url to string) are more specific than + // identity conversions (AKA left == right). That's because + // we want to ensure we convert urls to strings in binary + // expressions. + return IR::StringType; + } else if (left == right) + return left; + else if (isNumberType(left) && isNumberType(right)) + return qMax(left, right); + else if ((isNumberType(left) && isStringType(right)) || + (isNumberType(right) && isStringType(left))) + return IR::StringType; + else + return IR::InvalidType; +} + + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast<QSOperator::Op>(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + default: return OpInvalid; + } +} + +void Const::dump(QTextStream &out) +{ + out << value; +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(value) << '"'; +} + +QString String::escape(const QStringRef &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QLatin1String("\\n"); + else if (ch == QLatin1Char('\r')) + r += QLatin1String("\\r"); + else if (ch == QLatin1Char('\\')) + r += QLatin1String("\\\\"); + else if (ch == QLatin1Char('"')) + r += QLatin1String("\\\""); + else if (ch == QLatin1Char('\'')) + r += QLatin1String("\\'"); + else + r += ch; + } + return r; +} + +void Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint32 line, quint32 column) +{ + this->type = type; + this->base = base; + this->id = id; + this->symbol = symbol; + this->ptr = 0; + this->property = 0; + this->storage = MemberStorage; + this->builtin = NoBuiltinSymbol; + this->line = line; + this->column = column; + + if (id->length() == 8 && *id == QLatin1String("Math.sin")) { + builtin = MathSinBultinFunction; + } else if (id->length() == 8 && *id == QLatin1String("Math.cos")) { + builtin = MathCosBultinFunction; + } else if (id->length() == 10 && *id == QLatin1String("Math.round")) { + builtin = MathRoundBultinFunction; + } else if (id->length() == 10 && *id == QLatin1String("Math.floor)")) { + builtin = MathFloorBultinFunction; + } else if (id->length() == 7 && *id == QLatin1String("Math.PI")) { + builtin = MathPIBuiltinConstant; + this->type = RealType; + } +} + +void Name::dump(QTextStream &out) +{ + if (base) { + base->dump(out); + out << '.'; + } + + out << *id; +} + +void Temp::dump(QTextStream &out) +{ + out << 't' << index; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +Type Unop::typeForOp(AluOp op, Expr *expr) +{ + switch (op) { + case OpIfTrue: return BoolType; + case OpNot: return BoolType; + + case OpUMinus: + case OpUPlus: + case OpCompl: + return maxType(expr->type, RealType); + + default: + break; + } + + return InvalidType; +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) +{ + if (! (left && right)) + return InvalidType; + + switch (op) { + case OpInvalid: + return InvalidType; + + // unary operators + case OpIfTrue: + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + return InvalidType; + + // bit fields + case OpBitAnd: + case OpBitOr: + case OpBitXor: + return IntType; + + case OpAdd: + if (left->type == StringType) + return StringType; + return RealType; + + case OpSub: + case OpMul: + case OpDiv: + case OpMod: + return RealType; + + case OpLShift: + case OpRShift: + case OpURShift: + return IntType; + + case OpAnd: + case OpOr: + return BoolType; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + return BoolType; + } // switch + + return InvalidType; +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +Type Call::typeForFunction(Expr *base) +{ + if (! base) + return InvalidType; + + if (Name *name = base->asName()) { + switch (name->builtin) { + case MathSinBultinFunction: + case MathCosBultinFunction: + return RealType; + + case MathRoundBultinFunction: + case MathFloorBultinFunction: + return IntType; + + case NoBuiltinSymbol: + case MathPIBuiltinConstant: + break; + } + } // switch + + return InvalidType; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << " = "; + if (source->type != target->type) + out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); + if (source->type != target->type) + out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';'; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +Function::~Function() +{ + qDeleteAll(basicBlocks); +} + +QString *Function::newString(const QString &text) +{ + return pool->NewString(text); +} + +BasicBlock *Function::newBasicBlock() +{ + const int index = basicBlocks.size(); + return i(new BasicBlock(this, index)); +} + +void Function::dump(QTextStream &out) +{ + out << "function () {" << endl; + foreach (BasicBlock *bb, basicBlocks) { + bb->dump(out); + } + out << '}' << endl; +} + +Temp *BasicBlock::TEMP(Type type, int index) +{ + Temp *e = function->pool->New<Temp>(); + e->init(type, index); + return e; +} + +Temp *BasicBlock::TEMP(Type type) +{ + return TEMP(type, function->tempCount++); +} + +Expr *BasicBlock::CONST(double value) +{ + return CONST(IR::RealType, value); +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->pool->New<Const>(); + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QStringRef &value) +{ + String *e = function->pool->New<String>(); + e->init(value); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + return NAME(0, id, line, column); +} + +Name *BasicBlock::NAME(Name *base, const QString &id, quint32 line, quint32 column) +{ + Name *e = function->pool->New<Name>(); + e->init(base, InvalidType, + function->newString(id), + Name::Unbound, line, column); + return e; +} + +Name *BasicBlock::SYMBOL(Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = SYMBOL(/*base = */ 0, type, id, meta, property, line, column); + name->storage = storage; + return name; +} + +Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = function->pool->New<Name>(); + name->init(base, type, function->newString(id), + Name::Property, line, column); + name->meta = meta; + name->property = property; + name->storage = storage; + return name; +} + +Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, + quint32 line, quint32 column) +{ + Name *name = function->pool->New<Name>(); + name->init(base, type, function->newString(id), + Name::Property, line, column); + name->meta = meta; + name->property = property; + return name; +} + +Name *BasicBlock::ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint32 line, quint32 column) +{ + Name *name = function->pool->New<Name>(); + name->init(/*base = */ 0, IR::ObjectType, + function->newString(id), + Name::IdObject, line, column); + name->idObject = object; + name->property = 0; + name->storage = Name::IdStorage; + return name; +} + +Name *BasicBlock::ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = function->pool->New<Name>(); + name->init(/*base = */ 0, IR::AttachType, + function->newString(id), + Name::AttachType, line, column); + name->declarativeType = attachType; + name->storage = storage; + return name; +} + + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + Unop *e = function->pool->New<Unop>(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + if (left && right) { + if (Const *c1 = left->asConst()) { + if (Const *c2 = right->asConst()) { + switch (op) { + case OpAdd: return CONST(c1->value + c2->value); + case OpAnd: return CONST(c1->value ? c2->value : 0); + case OpBitAnd: return CONST(int(c1->value) & int(c2->value)); + case OpBitOr: return CONST(int(c1->value) | int(c2->value)); + case OpBitXor: return CONST(int(c1->value) ^ int(c2->value)); + case OpDiv: return CONST(c1->value / c2->value); + case OpEqual: return CONST(c1->value == c2->value); + case OpGe: return CONST(c1->value >= c2->value); + case OpGt: return CONST(c1->value > c2->value); + case OpLe: return CONST(c1->value <= c2->value); + case OpLShift: return CONST(int(c1->value) << int(c2->value)); + case OpLt: return CONST(c1->value < c2->value); + case OpMod: return CONST(::fmod(c1->value, c2->value)); + case OpMul: return CONST(c1->value * c2->value); + case OpNotEqual: return CONST(c1->value != c2->value); + case OpOr: return CONST(c1->value ? c1->value : c2->value); + case OpRShift: return CONST(int(c1->value) >> int(c2->value)); + case OpStrictEqual: return CONST(c1->value == c2->value); + case OpStrictNotEqual: return CONST(c1->value != c2->value); + case OpSub: return CONST(c1->value - c2->value); + case OpURShift: return CONST(unsigned(c1->value) >> int(c2->value)); + + case OpIfTrue: // unary ops + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + case OpInvalid: + break; + } + } + } + } + + Binop *e = function->pool->New<Binop>(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->pool->New<Call>(); + e->init(base, args); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + Exp *s = function->pool->New<Exp>(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn) +{ + Move *s = function->pool->New<Move>(); + s->init(target, source, isMoveForReturn); + statements.append(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->pool->New<Jump>(); + s->init(target); + statements.append(s); + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + CJump *s = function->pool->New<CJump>(); + s->init(cond, iftrue, iffalse); + statements.append(s); + return s; +} + +Stmt *BasicBlock::RET(Expr *expr, Type type, quint32 line, quint32 column) +{ + if (isTerminated()) + return 0; + + Ret *s = function->pool->New<Ret>(); + s->init(expr, type, line, column); + statements.append(s); + return s; +} + +void BasicBlock::dump(QTextStream &out) +{ + out << 'L' << this << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out); + out << endl; + } +} + +#ifdef DEBUG_IR_STRUCTURE + +static const char *symbolname(Name::Symbol s) +{ + switch (s) { + case Name::Unbound: + return "Unbound"; + case Name::IdObject: + return "IdObject"; + case Name::AttachType: + return "AttachType"; + case Name::Object: + return "Object"; + case Name::Property: + return "Property"; + case Name::Slot: + return "Slot"; + default: + Q_ASSERT(!"Unreachable"); + return "Unknown"; + } +} + +static const char *storagename(Name::Storage s) +{ + switch (s) { + case Name::MemberStorage: + return "MemberStorage"; + case Name::IdStorage: + return "IdStorage"; + case Name::RootStorage: + return "RootStorage"; + case Name::ScopeStorage: + return "ScopeStorage"; + default: + Q_ASSERT(!"Unreachable"); + return "UnknownStorage"; + } +} + +IRDump::IRDump() +: indentSize(0) +{ +} + +void IRDump::inc() +{ + indentSize++; + indentData = QByteArray(indentSize * 4, ' '); +} + +void IRDump::dec() +{ + indentSize--; + indentData = QByteArray(indentSize * 4, ' '); +} + +void IRDump::dec(); + +void IRDump::expression(QQmlJS::IR::Expr *e) +{ + inc(); + + e->accept(this); + + dec(); +} + +void IRDump::basicblock(QQmlJS::IR::BasicBlock *b) +{ + inc(); + + qWarning().nospace() << indent() << "BasicBlock " << b << " {"; + for (int ii = 0; ii < b->statements.count(); ++ii) { + statement(b->statements.at(ii)); + if (ii != (b->statements.count() - 1)) + qWarning(); + } + qWarning().nospace() << indent() << "}"; + + dec(); +} + +void IRDump::statement(QQmlJS::IR::Stmt *s) +{ + inc(); + + s->accept(this); + + dec(); +} + +void IRDump::function(QQmlJS::IR::Function *f) +{ + inc(); + + qWarning().nospace() << indent() << "Function {"; + for (int ii = 0; ii < f->basicBlocks.count(); ++ii) { + basicblock(f->basicBlocks.at(ii)); + } + qWarning().nospace() << indent() << "}"; + + dec(); +} + +const char *IRDump::indent() +{ + return indentData.constData(); +} + +void IRDump::visitConst(QQmlJS::IR::Const *e) +{ + qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}"; +} + +void IRDump::visitString(QQmlJS::IR::String *e) +{ + qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}"; +} + +static void namedumprecur(QQmlJS::IR::Name *e, const char *indent) +{ + if (e->base) namedumprecur(e->base, indent); + qWarning().nospace() << indent << " { type: " << typeName(e->type) << ", symbol: " << symbolname(e->symbol) << ", storage: " << storagename(e->storage) << ", id: " << e->id << "}"; +} + +void IRDump::visitName(QQmlJS::IR::Name *e) +{ + qWarning().nospace() << indent() << "Name:Expr {"; + namedumprecur(e, indent()); + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitTemp(QQmlJS::IR::Temp *e) +{ + qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }"; +} + +void IRDump::visitUnop(QQmlJS::IR::Unop *e) +{ + qWarning().nospace() << indent() << "Unop:Expr { "; + qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); + qWarning().nospace() << indent() << " expr: {"; + expression(e->expr); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitBinop(QQmlJS::IR::Binop *e) +{ + qWarning().nospace() << indent() << "Binop:Expr { "; + qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); + qWarning().nospace() << indent() << " left: {"; + inc(); + expression(e->left); + dec(); + qWarning().nospace() << indent() << " },"; + qWarning().nospace() << indent() << " right: {"; + inc(); + expression(e->right); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitCall(QQmlJS::IR::Call *e) +{ + Q_UNUSED(e); + qWarning().nospace() << indent() << "Exp::Call { }"; +} + +void IRDump::visitExp(QQmlJS::IR::Exp *s) +{ + qWarning().nospace() << indent() << "Exp:Stmt {"; + expression(s->expr); + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitMove(QQmlJS::IR::Move *s) +{ + qWarning().nospace() << indent() << "Move:Stmt {"; + qWarning().nospace() << indent() << " isMoveForReturn: " << s->isMoveForReturn; + qWarning().nospace() << indent() << " target: {"; + inc(); + expression(s->target); + dec(); + qWarning().nospace() << indent() << " },"; + qWarning().nospace() << indent() << " source: {"; + inc(); + expression(s->source); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitJump(QQmlJS::IR::Jump *s) +{ + qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }"; +} + +void IRDump::visitCJump(QQmlJS::IR::CJump *s) +{ + qWarning().nospace() << indent() << "CJump:Stmt {"; + qWarning().nospace() << indent() << " cond: {"; + inc(); + expression(s->cond); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << " iftrue: BasicBlock(" << s->iftrue << ")"; + qWarning().nospace() << indent() << " iffalse: BasicBlock(" << s->iffalse << ")"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitRet(QQmlJS::IR::Ret *s) +{ + qWarning().nospace() << indent() << "Ret:Stmt {"; + qWarning().nospace() << indent() << " type: " << typeName(s->type); + expression(s->expr); + qWarning().nospace() << indent() << "}"; +} +#endif + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h new file mode 100644 index 0000000000..48a08adf9f --- /dev/null +++ b/src/qml/qml/v4/qv4ir_p.h @@ -0,0 +1,604 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4IR_P_H +#define QV4IR_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/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> +#include <private/qqmlscript_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmlengine_p.h> +#include <private/qv4compiler_p.h> + +#include <private/qqmlpool_p.h> +#include <QtCore/qvarlengtharray.h> + +// #define DEBUG_IR_STRUCTURE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QQmlJS { + +namespace IR { + +struct BasicBlock; +struct Function; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct Name; +struct Temp; +struct Unop; +struct Binop; +struct Call; + +// statements +struct Exp; +struct Move; +struct Jump; +struct CJump; +struct Ret; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpAnd, + OpOr +}; +AluOp binaryOperator(int op); + +enum Type { + InvalidType, + UndefinedType, + NullType, + VoidType, + StringType, + UrlType, + SGAnchorLineType, + AttachType, + ObjectType, + + FirstNumberType, + BoolType = FirstNumberType, + IntType, + RealType, + RealNaNType +}; +Type maxType(IR::Type left, IR::Type right); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitUnop(Unop *) {} + virtual void visitBinop(Binop *) {} + virtual void visitCall(Call *) {} +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) {} + virtual void visitMove(Move *) {} + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *) {} + virtual void visitRet(Ret *) {} +}; + +struct Expr: QQmlPool::POD { + Type type; + + Expr(): type(InvalidType) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct ExprList: QQmlPool::POD { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + QStringRef value; + + void init(const QStringRef &value) + { + this->type = StringType; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QStringRef &s); +}; + +enum BuiltinSymbol { + NoBuiltinSymbol, + MathSinBultinFunction, + MathCosBultinFunction, + MathRoundBultinFunction, + MathFloorBultinFunction, + + MathPIBuiltinConstant +}; + +struct Name: Expr { + enum Symbol { + Unbound, + IdObject, // This is a load of a id object. Storage will always be IdStorage + AttachType, // This is a load of an attached object + Object, // XXX what is this for? + Property, // This is a load of a regular property + Slot // XXX what is this for? + }; + + enum Storage { + MemberStorage, // This is a property of a previously fetched object + IdStorage, // This is a load of a id object. Symbol will always be IdObject + RootStorage, // This is a property of the root object + ScopeStorage // This is a property of the scope object + }; + + Name *base; + const QString *id; + Symbol symbol; + union { + void *ptr; + const QMetaObject *meta; + const QQmlType *declarativeType; + const QQmlScript::Object *idObject; + }; + QQmlPropertyData *property; + Storage storage; + BuiltinSymbol builtin; + quint32 line; + quint32 column; + + void init(Name *base, Type type, const QString *id, Symbol symbol, quint32 line, quint32 column); + + inline bool is(Symbol s) const { return s == symbol; } + inline bool isNot(Symbol s) const { return s != symbol; } + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + int index; + + void init(Type type, int index) + { + this->type = type; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + void init(AluOp op, Expr *expr) + { + this->type = this->typeForOp(op, expr); + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForOp(AluOp op, Expr *expr); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; + Expr *right; + + void init(AluOp op, Expr *left, Expr *right) + { + this->type = typeForOp(op, left, right); + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForOp(AluOp op, Expr *left, Expr *right); +}; + +struct Call: Expr { + Expr *base; + ExprList *args; + + void init(Expr *base, ExprList *args) + { + this->type = typeForFunction(base); + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *base); +}; + +struct Stmt: QQmlPool::POD { + enum Mode { + HIR, + MIR + }; + + virtual ~Stmt() {} + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; + Expr *source; + bool isMoveForReturn; + + void init(Expr *target, Expr *source, bool isMoveForReturn) + { + this->target = target; + this->source = source; + this->isMoveForReturn = isMoveForReturn; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + Type type; + quint32 line; + quint32 column; + + void init(Expr *expr, Type type, quint32 line, quint32 column) + { + this->expr = expr; + this->type = type; + this->line = line; + this->column = column; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Function { + QQmlPool *pool; + QVarLengthArray<BasicBlock *, 8> basicBlocks; + int tempCount; + + Function(QQmlPool *pool) + : pool(pool), tempCount(0) {} + + ~Function(); + + BasicBlock *newBasicBlock(); + QString *newString(const QString &text); + + inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; } + + virtual void dump(QTextStream &out); +}; + +struct BasicBlock { + Function *function; + int index; + int offset; + QVarLengthArray<Stmt *, 32> statements; + + BasicBlock(Function *function, int index): function(function), index(index), offset(-1) {} + ~BasicBlock() {} + + template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + Temp *TEMP(Type type, int index); + Temp *TEMP(Type type); + + Expr *CONST(double value); + Expr *CONST(Type type, double value); + Expr *STRING(const QStringRef &value); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name *base, const QString &id, quint32 line, quint32 column); + Name *SYMBOL(Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage, quint32 line, quint32 column); + Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, quint32 line, quint32 column); + Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage, quint32 line, quint32 column); + Name *ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint32 line, quint32 column); + Name *ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage, quint32 line, quint32 column); + + Expr *UNOP(AluOp op, Expr *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args); + + Stmt *EXP(Expr *expr); + Stmt *MOVE(Expr *target, Expr *source, bool = false); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Expr *expr, Type type, quint32 line, quint32 column); + + virtual void dump(QTextStream &out); +}; + +#ifdef DEBUG_IR_STRUCTURE +struct IRDump : public ExprVisitor, + public StmtVisitor +{ +public: + IRDump(); + + void expression(QQmlJS::IR::Expr *); + void basicblock(QQmlJS::IR::BasicBlock *); + void statement(QQmlJS::IR::Stmt *); + void function(QQmlJS::IR::Function *); +protected: + + const char *indent(); + + // + // expressions + // + virtual void visitConst(QQmlJS::IR::Const *e); + virtual void visitString(QQmlJS::IR::String *e); + virtual void visitName(QQmlJS::IR::Name *e); + virtual void visitTemp(QQmlJS::IR::Temp *e); + virtual void visitUnop(QQmlJS::IR::Unop *e); + virtual void visitBinop(QQmlJS::IR::Binop *e); + virtual void visitCall(QQmlJS::IR::Call *e); + + // + // statements + // + virtual void visitExp(QQmlJS::IR::Exp *s); + virtual void visitMove(QQmlJS::IR::Move *s); + virtual void visitJump(QQmlJS::IR::Jump *s); + virtual void visitCJump(QQmlJS::IR::CJump *s); + virtual void visitRet(QQmlJS::IR::Ret *s); + +private: + int indentSize; + QByteArray indentData; + void inc(); + void dec(); +}; +#endif + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4IR_P_H diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp new file mode 100644 index 0000000000..1956be8e72 --- /dev/null +++ b/src/qml/qml/v4/qv4irbuilder.cpp @@ -0,0 +1,1303 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4irbuilder_p.h" +#include "qv4compiler_p_p.h" + +#include <private/qqmlglobal_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmltypenamecache_p.h> + +DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *engine, const QMetaObject * /* meta */) +{ + switch (t) { + case QMetaType::Bool: + return IR::BoolType; + + case QMetaType::Int: + return IR::IntType; + + case QMetaType::QReal: + return IR::RealType; + + case QMetaType::QString: + return IR::StringType; + + case QMetaType::QUrl: + return IR::UrlType; + + default: + if (t == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { + return IR::SGAnchorLineType; + } else if (engine->metaObjectForType(t)) { + return IR::ObjectType; + } + + return IR::InvalidType; + } +} + +QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr, + QQmlEnginePrivate *engine) +: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false) +{ +} + +bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function, + QQmlJS::AST::Node *ast) +{ + bool discarded = false; + + IR::BasicBlock *block = function->newBasicBlock(); + + qSwap(_discard, discarded); + qSwap(_function, function); + qSwap(_block, block); + + ExprResult r; + AST::SourceLocation location; + if (AST::ExpressionNode *asExpr = ast->expressionCast()) { + r = expression(asExpr); + location = asExpr->firstSourceLocation(); + } else if (AST::Statement *asStmt = ast->statementCast()) { + r = statement(asStmt); + location = asStmt->firstSourceLocation(); + } + + //_block->MOVE(_block->TEMP(IR::InvalidType), r.code); + if (r.code) { + const QMetaObject *m = 0; + const IR::Type targetType = irTypeFromVariantType(m_expression->property->type, m_engine, m); + if (targetType != r.type()) { + IR::Expr *x = _block->TEMP(targetType); + _block->MOVE(x, r, true); + r.code = x; + } + _block->RET(r.code, targetType, location.startLine, location.startColumn); + } + + qSwap(_block, block); + qSwap(_function, function); + qSwap(_discard, discarded); + + return !discarded; +} + +bool QV4IRBuilder::buildName(QList<QStringRef> &name, + AST::Node *node, + QList<AST::ExpressionNode *> *nodes) +{ + if (node->kind == AST::Node::Kind_IdentifierExpression) { + name << static_cast<AST::IdentifierExpression*>(node)->name; + if (nodes) *nodes << static_cast<AST::IdentifierExpression*>(node); + } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { + AST::FieldMemberExpression *expr = + static_cast<AST::FieldMemberExpression *>(node); + + if (!buildName(name, expr->base, nodes)) + return false; + + name << expr->name; + if (nodes) *nodes << expr; + } else { + return false; + } + + return true; +} + +void QV4IRBuilder::discard() +{ + _discard = true; +} + +QV4IRBuilder::ExprResult +QV4IRBuilder::expression(AST::ExpressionNode *ast) +{ + ExprResult r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.is(IR::InvalidType)) + discard(); + else { + Q_ASSERT(r.hint == r.format); + } + } + + return r; +} + +void QV4IRBuilder::condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (! ast) + return; + ExprResult r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.format != ExprResult::cx) { + if (! r.code) + discard(); + + Q_ASSERT(r.hint == ExprResult::cx); + Q_ASSERT(r.format == ExprResult::ex); + + if (r.type() != IR::BoolType) { + IR::Temp *t = _block->TEMP(IR::BoolType); + _block->MOVE(t, r); + r = t; + } + + _block->CJUMP(_block->UNOP(IR::OpIfTrue, r), iftrue, iffalse); + } +} + +QV4IRBuilder::ExprResult +QV4IRBuilder::statement(AST::Statement *ast) +{ + ExprResult r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.is(IR::InvalidType)) + discard(); + else { + Q_ASSERT(r.hint == r.format); + } + } + + return r; +} + +void QV4IRBuilder::sourceElement(AST::SourceElement *ast) +{ + accept(ast); +} + +void QV4IRBuilder::implicitCvt(ExprResult &expr, IR::Type type) +{ + if (expr.type() == type) + return; // nothing to do + + IR::Expr *x = _block->TEMP(type); + _block->MOVE(x, expr.code); + expr.code = x; +} + +// QML +bool QV4IRBuilder::visit(AST::UiProgram *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiImportList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiImport *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiPublicMember *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiSourceElement *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiObjectDefinition *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiObjectInitializer *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiObjectBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiScriptBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiArrayBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiObjectMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiArrayMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::UiQualifiedId *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// JS +bool QV4IRBuilder::visit(AST::Program *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::SourceElements *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::FunctionSourceElement *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::StatementSourceElement *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +// object literals +bool QV4IRBuilder::visit(AST::PropertyNameAndValueList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::IdentifierPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::StringLiteralPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::NumericLiteralPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// array literals +bool QV4IRBuilder::visit(AST::ElementList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::Elision *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// function calls +bool QV4IRBuilder::visit(AST::ArgumentList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +// expressions +bool QV4IRBuilder::visit(AST::ObjectLiteral *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ArrayLiteral *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ThisExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::IdentifierExpression *ast) +{ + const quint32 line = ast->identifierToken.startLine; + const quint32 column = ast->identifierToken.startColumn; + + const QString name = ast->name.toString(); + + if (name.at(0) == QLatin1Char('u') && name.length() == 9 && name == QLatin1String("undefined")) { + _expr.code = _block->CONST(IR::UndefinedType, 0); // ### undefined value + } else if (m_engine->v8engine()->illegalNames().contains(name) ) { + if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name; + return false; + } else if (const QQmlScript::Object *obj = m_expression->ids->value(name)) { + IR::Name *code = _block->ID_OBJECT(name, obj, line, column); + if (obj == m_expression->component) + code->storage = IR::Name::RootStorage; + _expr.code = code; + } else { + + QQmlTypeNameCache::Result r = m_expression->importCache->query(name); + if (r.isValid()) { + if (r.type) { + _expr.code = _block->ATTACH_TYPE(name, r.type, IR::Name::ScopeStorage, line, column); + } + // We don't support anything else + } else { + bool found = false; + + if (m_expression->context != m_expression->component) { + // RootStorage is more efficient than ScopeStorage, so prefer that if they are the same + QQmlPropertyCache *cache = m_expression->context->synthCache; + const QMetaObject *metaObject = m_expression->context->metaObject(); + if (!cache) cache = m_engine->cache(metaObject); + + QQmlPropertyData *data = cache->property(name); + + if (data && data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + if (data && !data->isFunction()) { + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject); + _expr.code = _block->SYMBOL(irType, name, metaObject, data, IR::Name::ScopeStorage, line, column); + found = true; + } + } + + if (!found) { + QQmlPropertyCache *cache = m_expression->component->synthCache; + const QMetaObject *metaObject = m_expression->component->metaObject(); + if (!cache) cache = m_engine->cache(metaObject); + + QQmlPropertyData *data = cache->property(name); + + if (data && data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + if (data && !data->isFunction()) { + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject); + _expr.code = _block->SYMBOL(irType, name, metaObject, data, IR::Name::RootStorage, line, column); + found = true; + } + } + + if (!found && qmlVerboseCompiler()) + qWarning() << "*** unknown symbol:" << name; + } + } + + if (_expr.code && _expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + + if (_expr.type() != IR::BoolType) { + IR::Temp *t = _block->TEMP(IR::BoolType); + _block->MOVE(t, _expr); + _expr.code = t; + } + + _block->CJUMP(_expr.code, _expr.iftrue, _expr.iffalse); + _expr.code = 0; + } + + return false; +} + +bool QV4IRBuilder::visit(AST::NullExpression *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::NullType, 0); + return false; +} + +bool QV4IRBuilder::visit(AST::TrueLiteral *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::BoolType, 1); + return false; +} + +bool QV4IRBuilder::visit(AST::FalseLiteral *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::BoolType, 0); + return false; +} + +bool QV4IRBuilder::visit(AST::StringLiteral *ast) +{ + // ### TODO: cx format + _expr.code = _block->STRING(ast->value); + return false; +} + +bool QV4IRBuilder::visit(AST::NumericLiteral *ast) +{ + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(ast->value ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(ast->value); + } + return false; +} + +bool QV4IRBuilder::visit(AST::RegExpLiteral *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::NestedExpression *) +{ + return true; // the value of the nested expression +} + +bool QV4IRBuilder::visit(AST::ArrayMemberExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast) +{ + if (IR::Expr *left = expression(ast->base)) { + if (IR::Name *baseName = left->asName()) { + const quint32 line = ast->identifierToken.startLine; + const quint32 column = ast->identifierToken.startColumn; + + QString name = ast->name.toString(); + + switch(baseName->symbol) { + case IR::Name::Unbound: + break; + + case IR::Name::AttachType: + if (name.at(0).isUpper()) { + QByteArray utf8Name = name.toUtf8(); + const char *enumName = utf8Name.constData(); + + const QMetaObject *meta = baseName->declarativeType->metaObject(); + bool found = false; + for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) { + QMetaEnum e = meta->enumerator(ii); + for (int jj = 0; !found && jj < e.keyCount(); ++jj) { + if (0 == strcmp(e.key(jj), enumName)) { + found = true; + _expr.code = _block->CONST(IR::IntType, e.value(jj)); + } + } + } + + if (!found && qmlVerboseCompiler()) + qWarning() << "*** unresolved enum:" + << (*baseName->id + QLatin1String(".") + ast->name.toString()); + } else if(const QMetaObject *attachedMeta = baseName->declarativeType->attachedPropertiesType()) { + QQmlPropertyCache *cache = m_engine->cache(attachedMeta); + QQmlPropertyData *data = cache->property(name); + + if (!data || data->isFunction()) + return false; // Don't support methods (or non-existing properties ;) + + if(!data->isFinal()) { + if (qmlVerboseCompiler()) + qWarning() << "*** non-final attached property:" + << (*baseName->id + QLatin1String(".") + ast->name.toString()); + return false; // We don't know enough about this property + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, attachedMeta); + _expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data, line, column); + } + break; + + case IR::Name::IdObject: { + const QQmlScript::Object *idObject = baseName->idObject; + QQmlPropertyCache *cache = + idObject->synthCache?idObject->synthCache:m_engine->cache(idObject->metaObject()); + + QQmlPropertyData *data = cache->property(name); + + if (!data || data->isFunction()) + return false; // Don't support methods (or non-existing properties ;) + + if (data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, idObject->metaObject()); + _expr.code = _block->SYMBOL(baseName, irType, name, + idObject->metaObject(), data, line, column); + } + break; + + case IR::Name::Property: + if (baseName->type == IR::ObjectType && baseName->meta && baseName->property->isFinal()) { + QQmlPropertyCache *cache = m_engine->cache(baseName->meta); + if (!cache) + return false; + + if (QQmlPropertyData *data = cache->property(name)) { + if (!data->isFinal()) { + if (qmlVerboseCompiler()) + qWarning() << "*** non-final property access:" + << (*baseName->id + QLatin1String(".") + ast->name.toString()); + return false; // We don't know enough about this property + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, baseName->meta); + _expr.code = _block->SYMBOL(baseName, irType, name, + baseName->meta, data, line, column); + } + } + break; + + case IR::Name::Object: + case IR::Name::Slot: + break; + } + } + } + + return false; +} + +bool QV4IRBuilder::preVisit(AST::Node *) +{ + return ! _discard; +} + +bool QV4IRBuilder::visit(AST::NewMemberExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::NewExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::CallExpression *ast) +{ + QList<QStringRef> names; + QList<AST::ExpressionNode *> nameNodes; + + names.reserve(4); + nameNodes.reserve(4); + + if (buildName(names, ast->base, &nameNodes)) { + //ExprResult base = expression(ast->base); + QString id; + for (int i = 0; i < names.size(); ++i) { + if (! i) + id += QLatin1Char('.'); + id += names.at(i); + } + const AST::SourceLocation loc = nameNodes.last()->firstSourceLocation(); + IR::Expr *base = _block->NAME(id, loc.startLine, loc.startColumn); + + IR::ExprList *args = 0, **argsInserter = &args; + for (AST::ArgumentList *it = ast->arguments; it; it = it->next) { + IR::Expr *arg = expression(it->expression); + *argsInserter = _function->pool->New<IR::ExprList>(); + (*argsInserter)->init(arg); + argsInserter = &(*argsInserter)->next; + } + + IR::Temp *r = _block->TEMP(IR::InvalidType); + IR::Expr *call = _block->CALL(base, args); + _block->MOVE(r, call); + r->type = call->type; + _expr.code = r; + } + + return false; +} + +bool QV4IRBuilder::visit(AST::PostIncrementExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::PostDecrementExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::DeleteExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::VoidExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::TypeOfExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::PreIncrementExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::PreDecrementExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::UnaryPlusExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (expr.code->asConst() != 0) { + _expr = expr; + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpUPlus, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QV4IRBuilder::visit(AST::UnaryMinusExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(-c->value); + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpUMinus, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QV4IRBuilder::visit(AST::TildeExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(~int(c->value)); + return false; + } + IR::Expr *code = _block->UNOP(IR::OpCompl, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QV4IRBuilder::visit(AST::NotExpression *ast) +{ + ExprResult expr = expression(ast->expression); + + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(!c->value); + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpNot, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + + } else if (expr.hint == ExprResult::cx) { + expr.format = ExprResult::cx; + _block->CJUMP(_block->UNOP(IR::OpNot, expr), _expr.iftrue, _expr.iffalse); + return false; + } + + return false; +} + +void QV4IRBuilder::binop(AST::BinaryExpression *ast, ExprResult left, ExprResult right) +{ + if (IR::Type t = maxType(left.type(), right.type())) { + implicitCvt(left, t); + implicitCvt(right, t); + + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->CJUMP(_block->BINOP(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + } + } +} + +bool QV4IRBuilder::visit(AST::BinaryExpression *ast) +{ + switch (ast->op) { + case QSOperator::And: { + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + + Q_ASSERT(_expr.iffalse != 0); + Q_ASSERT(_expr.iftrue != 0); + + IR::BasicBlock *iftrue = _function->newBasicBlock(); + condition(ast->left, iftrue, _expr.iffalse); + + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->left, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + _block = iffalse; + _block->MOVE(r, _block->CONST(0)); // ### use the right null value + _block->JUMP(endif); + + _block = iftrue; + ExprResult right = expression(ast->right); + _block->MOVE(r, right); + _block->JUMP(endif); + + _block = endif; + + r->type = right.type(); // ### not exactly, it can be IR::BoolType. + _expr.code = r; + } + } break; + + case QSOperator::Or: { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + ExprResult left = expression(ast->left); + IR::Temp *r = _block->TEMP(left.type()); + _block->MOVE(r, left); + + IR::Expr *cond = r; + if (r->type != IR::BoolType) { + cond = _block->TEMP(IR::BoolType); + _block->MOVE(cond, r); + } + + _block->CJUMP(_block->UNOP(IR::OpNot, cond), iftrue, endif); + + _block = iftrue; + ExprResult right = expression(ast->right); + _block->MOVE(r, right); + _block->JUMP(endif); + + if (left.type() != right.type()) + discard(); + + _expr.code = r; + + _block = endif; + } break; + + case QSOperator::Lt: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Ge: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if (left.type() == IR::StringType && right.type() == IR::StringType) { + binop(ast, left, right); + } else if (left.isValid() && right.isValid()) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + binop(ast, left, right); + } + } break; + + case QSOperator::NotEqual: + case QSOperator::Equal: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if ((left.type() == IR::NullType || left.type() == IR::UndefinedType) && + (right.type() == IR::NullType || right.type() == IR::UndefinedType)) { + const bool isEq = ast->op == QSOperator::Equal; + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0); + } + } else if ((left.type() == IR::StringType && right.type() >= IR::FirstNumberType) || + (left.type() >= IR::FirstNumberType && right.type() == IR::StringType)) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + binop(ast, left, right); + } else if (left.type() == IR::BoolType || right.type() == IR::BoolType) { + implicitCvt(left, IR::BoolType); + implicitCvt(right, IR::BoolType); + } else if (left.isValid() && right.isValid()) { + binop(ast, left, right); + } + } break; + + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if (left.type() == right.type()) { + binop(ast, left, right); + } else if (left.type() >= IR::BoolType && right.type() >= IR::BoolType) { + // left and right have numeric type (int or real) + binop(ast, left, right); + } else if (left.isValid() && right.isValid()) { + const bool isEq = ast->op == QSOperator::StrictEqual; + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0); + } + } + } break; + + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::LShift: + case QSOperator::RShift: + case QSOperator::URShift: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + implicitCvt(left, IR::IntType); + implicitCvt(right, IR::IntType); + + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + + } break; + + case QSOperator::Add: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + if (left.isPrimitive() && right.isPrimitive()) { + if (left.type() == IR::StringType || right.type() == IR::StringType) { + implicitCvt(left, IR::StringType); + implicitCvt(right, IR::StringType); + } + binop(ast, left, right); + } + } break; + + case QSOperator::Div: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::Sub: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + IR::Type t = maxType(left.type(), right.type()); + if (t >= IR::FirstNumberType) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + } + } break; + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Assign: + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: + // yup, we don't do those. + break; + } // switch + + return false; +} + +bool QV4IRBuilder::visit(AST::ConditionalExpression *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->expression, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + qSwap(_block, iftrue); + ExprResult ok = expression(ast->ok); + _block->MOVE(r, ok); + _block->JUMP(endif); + qSwap(_block, iftrue); + + qSwap(_block, iffalse); + ExprResult ko = expression(ast->ko); + _block->MOVE(r, ko); + _block->JUMP(endif); + qSwap(_block, iffalse); + + r->type = maxType(ok.type(), ko.type()); + _expr.code = r; + + _block = endif; + + return false; +} + +bool QV4IRBuilder::visit(AST::Expression *ast) +{ + _block->EXP(expression(ast->left)); + _expr = expression(ast->right); + + return false; +} + + +// statements +bool QV4IRBuilder::visit(AST::Block *ast) +{ + if (ast->statements && ! ast->statements->next) { + // we have one and only one statement + accept(ast->statements->statement); + } + + return false; +} + +bool QV4IRBuilder::visit(AST::StatementList *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::VariableStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::VariableDeclarationList *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::VariableDeclaration *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::EmptyStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ExpressionStatement *ast) +{ + if (ast->expression) { + // return the value of this expression + return true; + } + + return false; +} + +bool QV4IRBuilder::visit(AST::IfStatement *ast) +{ + if (! ast->ko) { + // This is an if statement without an else branch. + discard(); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->expression, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + qSwap(_block, iftrue); + ExprResult ok = statement(ast->ok); + _block->MOVE(r, ok); + _block->JUMP(endif); + qSwap(_block, iftrue); + + qSwap(_block, iffalse); + ExprResult ko = statement(ast->ko); + _block->MOVE(r, ko); + _block->JUMP(endif); + qSwap(_block, iffalse); + + r->type = maxType(ok.type(), ko.type()); + _expr.code = r; + + _block = endif; + } + + return false; +} + +bool QV4IRBuilder::visit(AST::DoWhileStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::WhileStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ForStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::LocalForStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ForEachStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::LocalForEachStatement *) +{ + discard(); + return false; +} + +bool QV4IRBuilder::visit(AST::ContinueStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::BreakStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ReturnStatement *ast) +{ + if (ast->expression) { + // return the value of the expression + return true; + } + + return false; +} + +bool QV4IRBuilder::visit(AST::WithStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::SwitchStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::CaseBlock *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::CaseClauses *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::CaseClause *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::DefaultClause *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::LabelledStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::ThrowStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::TryStatement *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::Catch *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::Finally *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::FunctionDeclaration *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::FunctionExpression *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::FormalParameterList *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::FunctionBody *) +{ + return false; +} + +bool QV4IRBuilder::visit(AST::DebuggerStatement *) +{ + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h new file mode 100644 index 0000000000..2b338c0778 --- /dev/null +++ b/src/qml/qml/v4/qv4irbuilder_p.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4IRBUILDER_P_H +#define QV4IRBUILDER_P_H + +#include <QtCore/qglobal.h> + +#include "qv4ir_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QV4IRBuilder : public QQmlJS::AST::Visitor +{ +public: + QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *); + + bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *); + +protected: + struct ExprResult { + enum Format { + ex, // expression + cx // condition + }; + + QQmlJS::IR::Expr *code; + QQmlJS::IR::BasicBlock *iftrue; + QQmlJS::IR::BasicBlock *iffalse; + Format hint; // requested format + Format format; // instruction format + + ExprResult(QQmlJS::IR::Expr *expr = 0) + : code(expr), iftrue(0), iffalse(0), hint(ex), format(ex) {} + + ExprResult(QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse) + : code(0), iftrue(iftrue), iffalse(iffalse), hint(cx), format(ex) {} + + inline QQmlJS::IR::Type type() const { return code ? code->type : QQmlJS::IR::InvalidType; } + + inline QQmlJS::IR::Expr *get() const { return code; } + inline operator QQmlJS::IR::Expr *() const { return get(); } + inline QQmlJS::IR::Expr *operator->() const { return get(); } + inline bool isValid() const { return code ? code->type != QQmlJS::IR::InvalidType : false; } + inline bool is(QQmlJS::IR::Type t) const { return type() == t; } + inline bool isNot(QQmlJS::IR::Type t) const { return type() != t; } + + bool isPrimitive() const { + switch (type()) { + case QQmlJS::IR::UndefinedType: // ### TODO + case QQmlJS::IR::NullType: // ### TODO + case QQmlJS::IR::UrlType: // ### TODO + return false; + + case QQmlJS::IR::StringType: + case QQmlJS::IR::BoolType: + case QQmlJS::IR::IntType: + case QQmlJS::IR::RealType: + case QQmlJS::IR::RealNaNType: + return true; + + default: + return false; + } // switch + } + }; + + inline void accept(QQmlJS::AST::Node *ast) { QQmlJS::AST::Node::accept(ast, this); } + + ExprResult expression(QQmlJS::AST::ExpressionNode *ast); + ExprResult statement(QQmlJS::AST::Statement *ast); + void sourceElement(QQmlJS::AST::SourceElement *ast); + void condition(QQmlJS::AST::ExpressionNode *ast, QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse); + void binop(QQmlJS::AST::BinaryExpression *ast, ExprResult left, ExprResult right); + + void implicitCvt(ExprResult &expr, QQmlJS::IR::Type type); + + virtual bool preVisit(QQmlJS::AST::Node *ast); + + // QML + virtual bool visit(QQmlJS::AST::UiProgram *ast); + virtual bool visit(QQmlJS::AST::UiImportList *ast); + virtual bool visit(QQmlJS::AST::UiImport *ast); + virtual bool visit(QQmlJS::AST::UiPublicMember *ast); + virtual bool visit(QQmlJS::AST::UiSourceElement *ast); + virtual bool visit(QQmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QQmlJS::AST::UiObjectInitializer *ast); + virtual bool visit(QQmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QQmlJS::AST::UiScriptBinding *ast); + virtual bool visit(QQmlJS::AST::UiArrayBinding *ast); + virtual bool visit(QQmlJS::AST::UiObjectMemberList *ast); + virtual bool visit(QQmlJS::AST::UiArrayMemberList *ast); + virtual bool visit(QQmlJS::AST::UiQualifiedId *ast); + + // JS + virtual bool visit(QQmlJS::AST::Program *ast); + virtual bool visit(QQmlJS::AST::SourceElements *ast); + virtual bool visit(QQmlJS::AST::FunctionSourceElement *ast); + virtual bool visit(QQmlJS::AST::StatementSourceElement *ast); + + // object literals + virtual bool visit(QQmlJS::AST::PropertyNameAndValueList *ast); + virtual bool visit(QQmlJS::AST::IdentifierPropertyName *ast); + virtual bool visit(QQmlJS::AST::StringLiteralPropertyName *ast); + virtual bool visit(QQmlJS::AST::NumericLiteralPropertyName *ast); + + // array literals + virtual bool visit(QQmlJS::AST::ElementList *ast); + virtual bool visit(QQmlJS::AST::Elision *ast); + + // function calls + virtual bool visit(QQmlJS::AST::ArgumentList *ast); + + // expressions + virtual bool visit(QQmlJS::AST::ObjectLiteral *ast); + virtual bool visit(QQmlJS::AST::ArrayLiteral *ast); + virtual bool visit(QQmlJS::AST::ThisExpression *ast); + virtual bool visit(QQmlJS::AST::IdentifierExpression *ast); + virtual bool visit(QQmlJS::AST::NullExpression *ast); + virtual bool visit(QQmlJS::AST::TrueLiteral *ast); + virtual bool visit(QQmlJS::AST::FalseLiteral *ast); + virtual bool visit(QQmlJS::AST::StringLiteral *ast); + virtual bool visit(QQmlJS::AST::NumericLiteral *ast); + virtual bool visit(QQmlJS::AST::RegExpLiteral *ast); + virtual bool visit(QQmlJS::AST::NestedExpression *ast); + virtual bool visit(QQmlJS::AST::ArrayMemberExpression *ast); + virtual bool visit(QQmlJS::AST::FieldMemberExpression *ast); + virtual bool visit(QQmlJS::AST::NewMemberExpression *ast); + virtual bool visit(QQmlJS::AST::NewExpression *ast); + virtual bool visit(QQmlJS::AST::CallExpression *ast); + virtual bool visit(QQmlJS::AST::PostIncrementExpression *ast); + virtual bool visit(QQmlJS::AST::PostDecrementExpression *ast); + virtual bool visit(QQmlJS::AST::DeleteExpression *ast); + virtual bool visit(QQmlJS::AST::VoidExpression *ast); + virtual bool visit(QQmlJS::AST::TypeOfExpression *ast); + virtual bool visit(QQmlJS::AST::PreIncrementExpression *ast); + virtual bool visit(QQmlJS::AST::PreDecrementExpression *ast); + virtual bool visit(QQmlJS::AST::UnaryPlusExpression *ast); + virtual bool visit(QQmlJS::AST::UnaryMinusExpression *ast); + virtual bool visit(QQmlJS::AST::TildeExpression *ast); + virtual bool visit(QQmlJS::AST::NotExpression *ast); + virtual bool visit(QQmlJS::AST::BinaryExpression *ast); + virtual bool visit(QQmlJS::AST::ConditionalExpression *ast); + virtual bool visit(QQmlJS::AST::Expression *ast); + + // statements + virtual bool visit(QQmlJS::AST::Block *ast); + virtual bool visit(QQmlJS::AST::StatementList *ast); + virtual bool visit(QQmlJS::AST::VariableStatement *ast); + virtual bool visit(QQmlJS::AST::VariableDeclarationList *ast); + virtual bool visit(QQmlJS::AST::VariableDeclaration *ast); + virtual bool visit(QQmlJS::AST::EmptyStatement *ast); + virtual bool visit(QQmlJS::AST::ExpressionStatement *ast); + virtual bool visit(QQmlJS::AST::IfStatement *ast); + virtual bool visit(QQmlJS::AST::DoWhileStatement *ast); + virtual bool visit(QQmlJS::AST::WhileStatement *ast); + virtual bool visit(QQmlJS::AST::ForStatement *ast); + virtual bool visit(QQmlJS::AST::LocalForStatement *ast); + virtual bool visit(QQmlJS::AST::ForEachStatement *ast); + virtual bool visit(QQmlJS::AST::LocalForEachStatement *ast); + virtual bool visit(QQmlJS::AST::ContinueStatement *ast); + virtual bool visit(QQmlJS::AST::BreakStatement *ast); + virtual bool visit(QQmlJS::AST::ReturnStatement *ast); + virtual bool visit(QQmlJS::AST::WithStatement *ast); + virtual bool visit(QQmlJS::AST::SwitchStatement *ast); + virtual bool visit(QQmlJS::AST::CaseBlock *ast); + virtual bool visit(QQmlJS::AST::CaseClauses *ast); + virtual bool visit(QQmlJS::AST::CaseClause *ast); + virtual bool visit(QQmlJS::AST::DefaultClause *ast); + virtual bool visit(QQmlJS::AST::LabelledStatement *ast); + virtual bool visit(QQmlJS::AST::ThrowStatement *ast); + virtual bool visit(QQmlJS::AST::TryStatement *ast); + virtual bool visit(QQmlJS::AST::Catch *ast); + virtual bool visit(QQmlJS::AST::Finally *ast); + virtual bool visit(QQmlJS::AST::FunctionDeclaration *ast); + virtual bool visit(QQmlJS::AST::FunctionExpression *ast); + virtual bool visit(QQmlJS::AST::FormalParameterList *ast); + virtual bool visit(QQmlJS::AST::FunctionBody *ast); + virtual bool visit(QQmlJS::AST::DebuggerStatement *ast); + +private: + bool buildName(QList<QStringRef> &name, QQmlJS::AST::Node *node, + QList<QQmlJS::AST::ExpressionNode *> *nodes); + void discard(); + + const QV4Compiler::Expression *m_expression; + QQmlEnginePrivate *m_engine; + + QQmlJS::IR::Function *_function; + QQmlJS::IR::BasicBlock *_block; + bool _discard; + + ExprResult _expr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4IRBUILDER_P_H diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4program_p.h new file mode 100644 index 0000000000..b6b03e438b --- /dev/null +++ b/src/qml/qml/v4/qv4program_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4PROGRAM_P_H +#define QV4PROGRAM_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 "qv4instruction_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct QV4Program { + quint32 bindings; + quint32 dataLength; + quint32 signalTableOffset; + quint32 exceptionDataOffset; + quint16 subscriptions; + quint16 identifiers; + quint16 instructionCount; + + struct BindingReference { + quint32 binding; + quint32 blockMask; + }; + + struct BindingReferenceList { + quint32 count; + BindingReference bindings[]; + }; + + inline const char *data() const; + inline const char *instructions() const; + inline BindingReferenceList *signalTable(int signalIndex) const; +}; + +enum QQmlRegisterType { + UndefinedType, + QObjectStarType, + QRealType, + IntType, + BoolType, + + PODValueType, + + FirstCleanupType, + QStringType = FirstCleanupType, + QUrlType, + QVariantType, +}; + +const char *QV4Program::data() const +{ + return ((const char *)this) + sizeof(QV4Program); +} + +const char *QV4Program::instructions() const +{ + return (const char *)(data() + dataLength); +} + +QV4Program::BindingReferenceList *QV4Program::signalTable(int signalIndex) const +{ + quint32 *signalTable = (quint32 *)(data() + signalTableOffset); + return (BindingReferenceList *)(signalTable + signalTable[signalIndex]); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4PROGRAM_P_H + diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri new file mode 100644 index 0000000000..b6784851d8 --- /dev/null +++ b/src/qml/qml/v4/v4.pri @@ -0,0 +1,15 @@ +HEADERS += \ + $$PWD/qv4compiler_p.h \ + $$PWD/qv4compiler_p_p.h \ + $$PWD/qv4ir_p.h \ + $$PWD/qv4irbuilder_p.h \ + $$PWD/qv4instruction_p.h \ + $$PWD/qv4bindings_p.h \ + $$PWD/qv4program_p.h \ + +SOURCES += \ + $$PWD/qv4compiler.cpp \ + $$PWD/qv4ir.cpp \ + $$PWD/qv4irbuilder.cpp \ + $$PWD/qv4instruction.cpp \ + $$PWD/qv4bindings.cpp \ diff --git a/src/qml/qml/v8/notes.txt b/src/qml/qml/v8/notes.txt new file mode 100644 index 0000000000..a4006b93c6 --- /dev/null +++ b/src/qml/qml/v8/notes.txt @@ -0,0 +1,4 @@ +Removed backwards compatible imports - QTBUG-17518 + +autotest print() taking objects that don't ToString() +autotest QQmlV8Function diff --git a/src/qml/qml/v8/qjsconverter_impl_p.h b/src/qml/qml/v8/qjsconverter_impl_p.h new file mode 100644 index 0000000000..10b8ab5fae --- /dev/null +++ b/src/qml/qml/v8/qjsconverter_impl_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsconverter_p.h" + +#ifndef QJSCONVERTER_IMPL_P_H +#define QJSCONVERTER_IMPL_P_H + +QT_BEGIN_NAMESPACE + +extern char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + + +quint32 QJSConverter::toArrayIndex(const QString& string) +{ + // FIXME this function should be exported by JSC C API. + bool ok; + quint32 idx = string.toUInt(&ok); + if (!ok || toString(idx) != string) + idx = 0xffffffff; + + return idx; +} + +QString QJSConverter::toString(v8::Handle<v8::String> jsString) +{ + if (jsString.IsEmpty()) + return QString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + return qstr; +} + +v8::Local<v8::String> QJSConverter::toString(const QString& string) +{ + return v8::String::New(reinterpret_cast<const uint16_t*>(string.data()), string.size()); +} + +QString QJSConverter::toString(double value) +{ + // FIXME this should be easier. The ideal fix is to create + // a new function in V8 API which could cover the functionality. + + if (qIsNaN(value)) + return QString::fromLatin1("NaN"); + if (qIsInf(value)) + return QString::fromLatin1(value < 0 ? "-Infinity" : "Infinity"); + if (!value) + return QString::fromLatin1("0"); + + QVarLengthArray<char, 25> buf; + int decpt; + int sign; + char* result = 0; + char* endresult; + (void)qdtoa(value, 0, 0, &decpt, &sign, &endresult, &result); + + if (!result) + return QString(); + + int resultLen = endresult - result; + if (decpt <= 0 && decpt > -6) { + buf.resize(-decpt + 2 + sign); + qMemSet(buf.data(), '0', -decpt + 2 + sign); + if (sign) // fix the sign. + buf[0] = '-'; + buf[sign + 1] = '.'; + buf.append(result, resultLen); + } else { + if (sign) + buf.append('-'); + int length = buf.size() - sign + resultLen; + if (decpt <= 21 && decpt > 0) { + if (length <= decpt) { + const char* zeros = "0000000000000000000000000"; + buf.append(result, resultLen); + buf.append(zeros, decpt - length); + } else { + buf.append(result, decpt); + buf.append('.'); + buf.append(result + decpt, resultLen - decpt); + } + } else if (result[0] >= '0' && result[0] <= '9') { + if (length > 1) { + buf.append(result, 1); + buf.append('.'); + buf.append(result + 1, resultLen - 1); + } else + buf.append(result, resultLen); + buf.append('e'); + buf.append(decpt >= 0 ? '+' : '-'); + int e = qAbs(decpt - 1); + if (e >= 100) + buf.append('0' + e / 100); + if (e >= 10) + buf.append('0' + (e % 100) / 10); + buf.append('0' + e % 10); + } + } + free(result); + buf.append(0); + return QString::fromLatin1(buf.constData()); +} + +// return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter +uint QJSConverter::toPropertyAttributes(const QFlags<QJSValuePrivate::PropertyFlag>& flags) +{ + uint attr = 0; + if (flags.testFlag(QJSValuePrivate::ReadOnly)) + attr |= v8::ReadOnly; + if (flags.testFlag(QJSValuePrivate::Undeletable)) + attr |= v8::DontDelete; + if (flags.testFlag(QJSValuePrivate::SkipInEnumeration)) + attr |= v8::DontEnum; + // if (flags.testFlag(QScriptValue::PropertyGetter)) + // attr |= QScriptValue::PropertyGetter; + // if (flags.testFlag(QScriptValue::PropertySetter)) + // attr |= QScriptValue::PropertySetter; + return attr; +} + +// Converts a JS RegExp to a QRegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +QRegExp QJSConverter::toRegExp(v8::Handle<v8::RegExp> jsRegExp) +{ + QString pattern = QJSConverter::toString(jsRegExp->GetSource()); + Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive; + if (jsRegExp->GetFlags() & v8::RegExp::kIgnoreCase) + caseSensitivity = Qt::CaseInsensitive; + return QRegExp(pattern, caseSensitivity, QRegExp::RegExp2); +} + +// Converts a QRegExp to a JS RegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +v8::Local<v8::RegExp> QJSConverter::toRegExp(const QRegExp &re) +{ + // Convert the pattern to a ECMAScript pattern. + QString pattern = qt_regexp_toCanonical(re.pattern(), re.patternSyntax()); + if (re.isMinimal()) { + QString ecmaPattern; + int len = pattern.length(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; + } + } + pattern = ecmaPattern; + } + + int flags = v8::RegExp::kNone; + if (re.caseSensitivity() == Qt::CaseInsensitive) + flags |= v8::RegExp::kIgnoreCase; + + return v8::RegExp::New(QJSConverter::toString(pattern), static_cast<v8::RegExp::Flags>(flags)); +} + +// Converts a QStringList to JS. +// The result is a new Array object with length equal to the length +// of the QStringList, and the elements being the QStringList's +// elements converted to JS Strings. +v8::Local<v8::Array> QJSConverter::toStringList(const QStringList &lst) +{ + v8::Local<v8::Array> result = v8::Array::New(lst.size()); + for (int i = 0; i < lst.size(); ++i) + result->Set(i, toString(lst.at(i))); + return result; +} + +// Converts a JS Array object to a QStringList. +// The result is a QStringList with length equal to the length +// of the JS Array, and elements being the JS Array's elements +// converted to QStrings. +QStringList QJSConverter::toStringList(v8::Handle<v8::Array> jsArray) +{ + QStringList result; + uint32_t length = jsArray->Length(); + for (uint32_t i = 0; i < length; ++i) + result.append(toString(jsArray->Get(i)->ToString())); + return result; +} + + +// Converts a JS Date to a QDateTime. +QDateTime QJSConverter::toDateTime(v8::Handle<v8::Date> jsDate) +{ + return QDateTime::fromMSecsSinceEpoch(jsDate->NumberValue()); +} + +// Converts a QDateTime to a JS Date. +v8::Local<v8::Value> QJSConverter::toDateTime(const QDateTime &dt) +{ + double date; + if (!dt.isValid()) + date = qSNaN(); + else + date = dt.toMSecsSinceEpoch(); + return v8::Date::New(date); +} + +QT_END_NAMESPACE + +#endif // QJSCONVERTER_IMPL_P_H diff --git a/src/qml/qml/v8/qjsconverter_p.h b/src/qml/qml/v8/qjsconverter_p.h new file mode 100644 index 0000000000..29fef3c700 --- /dev/null +++ b/src/qml/qml/v8/qjsconverter_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSCONVERTER_P_H +#define QJSCONVERTER_P_H + +#include "qjsvalue_p.h" +#include <QtCore/qglobal.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qregexp.h> +#include <QtCore/qdatetime.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +/* + \internal + \class QJSConverter + QJSValue and QJSEngine helper class. This class's responsibility is to convert values + between JS values and Qt/C++ values. + + This is a nice way to inline these functions in both QJSValue and QJSEngine. +*/ +class QJSConverter { +public: + static inline quint32 toArrayIndex(const QString& string); + + static inline QString toString(v8::Handle<v8::String> jsString); + static inline v8::Local<v8::String> toString(const QString& string); + static inline QString toString(double value); + + enum { + PropertyAttributeMask = v8::ReadOnly | v8::DontDelete | v8::DontEnum, + }; + // return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter + static inline uint toPropertyAttributes(const QFlags<QJSValuePrivate::PropertyFlag>& flags); + + // Converts a JS RegExp to a QRegExp. + // The conversion is not 100% exact since ECMA regexp and QRegExp + // have different semantics/flags, but we try to do our best. + static inline QRegExp toRegExp(v8::Handle<v8::RegExp> jsRegExp); + + // Converts a QRegExp to a JS RegExp. + // The conversion is not 100% exact since ECMA regexp and QRegExp + // have different semantics/flags, but we try to do our best. + static inline v8::Local<v8::RegExp> toRegExp(const QRegExp &re); + + // Converts a QStringList to JS. + // The result is a new Array object with length equal to the length + // of the QStringList, and the elements being the QStringList's + // elements converted to JS Strings. + static inline v8::Local<v8::Array> toStringList(const QStringList &lst); + + // Converts a JS Array object to a QStringList. + // The result is a QStringList with length equal to the length + // of the JS Array, and elements being the JS Array's elements + // converted to QStrings. + static inline QStringList toStringList(v8::Handle<v8::Array> jsArray); + + // Converts a JS Date to a QDateTime. + static inline QDateTime toDateTime(v8::Handle<v8::Date> jsDate); + + // Converts a QDateTime to a JS Date. + static inline v8::Local<v8::Value> toDateTime(const QDateTime &dt); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsengine.cpp b/src/qml/qml/v8/qjsengine.cpp new file mode 100644 index 0000000000..3121d1b361 --- /dev/null +++ b/src/qml/qml/v8/qjsengine.cpp @@ -0,0 +1,476 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsengine.h" +#include "qjsengine_p.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscriptisolate_p.h" +#include "qscript_impl_p.h" +#include "qv8engine_p.h" + +#include <QtCore/qdatetime.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvariant.h> +#include <QtCore/qdatetime.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpluginloader.h> +#include <qthread.h> +#include <qmutex.h> +#include <qwaitcondition.h> + +#undef Q_D +#undef Q_Q +#define Q_D(blah) +#define Q_Q(blah) + +Q_DECLARE_METATYPE(QJSValue) +Q_DECLARE_METATYPE(QObjectList) +Q_DECLARE_METATYPE(QList<int>) + +/*! + \since 5.0 + \class QJSEngine + + \brief The QJSEngine class provides an environment for evaluating JavaScript code. + + \ingroup qtjavascript + \mainclass + + \section1 Evaluating Scripts + + Use evaluate() to evaluate script code. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 0 + + evaluate() returns a QJSValue that holds the result of the + evaluation. The QJSValue class provides functions for converting + the result to various C++ types (e.g. QJSValue::toString() + and QJSValue::toNumber()). + + The following code snippet shows how a script function can be + defined and then invoked from C++ using QJSValue::call(): + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 1 + + As can be seen from the above snippets, a script is provided to the + engine in the form of a string. One common way of loading scripts is + by reading the contents of a file and passing it to evaluate(): + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 2 + + Here we pass the name of the file as the second argument to + evaluate(). This does not affect evaluation in any way; the second + argument is a general-purpose string that is used to identify the + script for debugging purposes (for example, our filename will now + show up in any uncaughtExceptionBacktrace() involving the script). + + \section1 Engine Configuration + + The globalObject() function returns the \bold {Global Object} + associated with the script engine. Properties of the Global Object + are accessible from any script code (i.e. they are global + variables). Typically, before evaluating "user" scripts, you will + want to configure a script engine by adding one or more properties + to the Global Object: + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 3 + + Adding custom properties to the scripting environment is one of the + standard means of providing a scripting API that is specific to your + application. Usually these custom properties are objects created by + the newQObject() or newObject() functions. + + \section1 Script Exceptions + + evaluate() can throw a script exception (e.g. due to a syntax + error); in that case, the return value is the value that was thrown + (typically an \c{Error} object). You can check whether the + evaluation caused an exception by calling hasUncaughtException(). In + that case, you can call toString() on the error object to obtain an + error message. The current uncaught exception is also available + through uncaughtException(). + Calling clearExceptions() will cause any uncaught exceptions to be + cleared. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 4 + + \section1 Script Object Creation + + Use newObject() to create a JavaScript object; this is the + C++ equivalent of the script statement \c{new Object()}. You can use + the object-specific functionality in QJSValue to manipulate the + script object (e.g. QJSValue::setProperty()). Similarly, use + newArray() to create a JavaScript array object. + + \section1 QObject Integration + + Use newQObject() to wrap a QObject (or subclass) + pointer. newQObject() returns a proxy script object; properties, + children, and signals and slots of the QObject are available as + properties of the proxy object. No binding code is needed because it + is done dynamically using the Qt meta object system. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 5 + + \sa QJSValue, {Making Applications Scriptable} + +*/ + +QT_BEGIN_NAMESPACE + + +/*! + Constructs a QJSEngine object. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ +QJSEngine::QJSEngine() + : d(new QV8Engine(this)) +{ +} + +#ifdef QT_DEPRECATED + +/*! + \internal +*/ +QJSEngine::QJSEngine(QJSEngine::ContextOwnership ownership) + : d(new QV8Engine(this, ownership)) +{ +} + +#endif // QT_DEPRECATED + +/*! + Constructs a QJSEngine object with the given \a parent. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ + +QJSEngine::QJSEngine(QObject *parent) + : QObject(parent) + , d(new QV8Engine(this)) +{ +} + +QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) + : QObject(dd, parent) + , d(new QV8Engine(this)) +{ +} + +/*! + Destroys this QJSEngine. +*/ +QJSEngine::~QJSEngine() +{ + delete d; +} + +/*! + \fn QV8Engine *QJSEngine::handle() const + \internal +*/ + +#ifdef QT_DEPRECATED + +/*! + \obsolete + + Returns true if the last script evaluation resulted in an uncaught + exception; otherwise returns false. + + The exception state is cleared when evaluate() is called. + + \sa uncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +bool QJSEngine::hasUncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->hasUncaughtException(); +} + +/*! + \obsolete + + Returns the current uncaught exception, or an invalid QJSValue + if there is no uncaught exception. + + The exception value is typically an \c{Error} object; in that case, + you can call toString() on the return value to obtain an error + message. + + \sa hasUncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +QJSValue QJSEngine::uncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->scriptValueFromInternal(d->uncaughtException()); +} + +/*! + \obsolete + + Clears any uncaught exceptions in this engine. + + \sa hasUncaughtException() +*/ +void QJSEngine::clearExceptions() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->clearExceptions(); +} + +#endif // QT_DEPRECATED + +/*! + Runs the garbage collector. + + The garbage collector will attempt to reclaim memory by locating and disposing of objects that are + no longer reachable in the script environment. + + Normally you don't need to call this function; the garbage collector will automatically be invoked + when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects + have been created). However, you can call this function to explicitly request that garbage + collection should be performed as soon as possible. + + \sa reportAdditionalMemoryCost() +*/ +void QJSEngine::collectGarbage() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->collectGarbage(); +} + +/*! + Evaluates \a program, using \a lineNumber as the base line number, + and returns the result of the evaluation. + + The script code will be evaluated in the current context. + + The evaluation of \a program can cause an exception in the + engine; in this case the return value will be the exception + that was thrown (typically an \c{Error} object). You can call + hasUncaughtException() to determine if an exception occurred in + the last call to evaluate(). + + \a lineNumber is used to specify a starting line number for \a + program; line number information reported by the engine that pertain + to this evaluation (e.g. uncaughtExceptionLineNumber()) will be + based on this argument. For example, if \a program consists of two + lines of code, and the statement on the second line causes a script + exception, uncaughtExceptionLineNumber() would return the given \a + lineNumber plus one. When no starting line number is specified, line + numbers will be 1-based. + + \a fileName is used for error reporting. For example in error objects + the file name is accessible through the "fileName" property if it's + provided with this function. +*/ +QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->evaluate(program, fileName, lineNumber)); +} + +/*! + Creates a JavaScript object of class Object. + + The prototype of the created object will be the Object + prototype object. + + \sa newArray(), QJSValue::setProperty() +*/ +QJSValue QJSEngine::newObject() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Object::New())); +} + +/*! + Creates a JavaScript object of class Array with the given \a length. + + \sa newObject() +*/ +QJSValue QJSEngine::newArray(uint length) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newArray(length)); +} + +/*! + Creates a JavaScript object that wraps the given QObject \a + object, using JavaScriptOwnership. The given \a options control + various aspects of the interaction with the resulting script object. + + Signals and slots, properties and children of \a object are + available as properties of the created QJSValue. + + If \a object is a null pointer, this function returns a null value. + + If a default prototype has been registered for the \a object's class + (or its superclass, recursively), the prototype of the new script + object will be set to be that default prototype. + + If the given \a object is deleted outside of the engine's control, any + attempt to access the deleted QObject's members through the JavaScript + wrapper object (either by script code or C++) will result in a + script exception. + + \sa QJSValue::toQObject(), reportAdditionalMemoryCost() +*/ +QJSValue QJSEngine::newQObject(QObject *object) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->newQObject(object, QV8Engine::JavaScriptOwnership)); +} + +/*! + Returns this engine's Global Object. + + By default, the Global Object contains the built-in objects that are + part of \l{ECMA-262}, such as Math, Date and String. Additionally, + you can set properties of the Global Object to make your own + extensions available to all script code. Non-local variables in + script code will be created as properties of the Global Object, as + well as local variables in global code. +*/ +QJSValue QJSEngine::globalObject() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->global()); +} + +/*! + * \internal + * used by QJSEngine::toScriptValue + */ +QJSValue QJSEngine::create(int type, const void *ptr) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->metaTypeToJS(type, ptr)); +} + +/*! + \internal + \since 4.5 + convert \a value to \a type, store the result in \a ptr +*/ +bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) +{ + QJSValuePrivate *vp = QJSValuePrivate::get(value); + QV8Engine *engine = vp->engine(); + if (engine) { + QScriptIsolate api(engine, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return engine->metaTypeFromJS(*vp, type, ptr); + } else { + switch (type) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(ptr) = vp->toBool(); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = vp->toUInt32(); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = vp->toInteger(); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = vp->toInteger(); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = vp->toNumber(); + return true; + case QMetaType::QString: + *reinterpret_cast<QString*>(ptr) = vp->toString(); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = vp->toNumber(); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = vp->toUInt16(); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = vp->toUInt16(); + return true; + case QMetaType::QChar: + *reinterpret_cast<QChar*>(ptr) = vp->toUInt16(); + return true; + default: + return false; + } + } +} + +/*! \fn QJSValue QJSEngine::toScriptValue(const T &value) + + Creates a QJSValue with the given \a value. + + \sa fromScriptValue() +*/ + +/*! \fn T QJSEngine::fromScriptValue(const QJSValue &value) + + Returns the given \a value converted to the template type \c{T}. + + \sa toScriptValue() +*/ + +QT_END_NAMESPACE + +#include "moc_qjsengine.cpp" diff --git a/src/qml/qml/v8/qjsengine.h b/src/qml/qml/v8/qjsengine.h new file mode 100644 index 0000000000..1521c752d4 --- /dev/null +++ b/src/qml/qml/v8/qjsengine.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_H +#define QJSENGINE_H + +#include <QtCore/qmetatype.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qobject.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QV8Engine; + +template <typename T> +inline T qjsvalue_cast(const QJSValue &); + +class QJSEnginePrivate; +class Q_QML_EXPORT QJSEngine + : public QObject +{ + Q_OBJECT +public: +#ifdef QT_DEPRECATED + enum ContextOwnership { + AdoptCurrentContext, + CreateNewContext + }; + QT_DEPRECATED explicit QJSEngine(ContextOwnership ownership); +#endif + + QJSEngine(); + explicit QJSEngine(QObject *parent); + virtual ~QJSEngine(); + + QJSValue globalObject() const; + + QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + + QJSValue newObject(); + QJSValue newArray(uint length = 0); + + QJSValue newQObject(QObject *object); + + template <typename T> + inline QJSValue toScriptValue(const T &value) + { + return create(qMetaTypeId<T>(), &value); + } + template <typename T> + inline T fromScriptValue(const QJSValue &value) + { + return qjsvalue_cast<T>(value); + } + + void collectGarbage(); + + QV8Engine *handle() const { return d; } + +#ifdef QT_DEPRECATED + QT_DEPRECATED bool hasUncaughtException() const; + QT_DEPRECATED QJSValue uncaughtException() const; + QT_DEPRECATED void clearExceptions(); +#endif + +Q_SIGNALS: + void signalHandlerException(const QJSValue &exception); + +private: + QJSValue create(int type, const void *ptr); + + static bool convertV2(const QJSValue &value, int type, void *ptr); + + friend inline bool qjsvalue_cast_helper(const QJSValue &, int, void *); + +protected: + QJSEngine(QJSEnginePrivate &dd, QObject *parent = 0); + +private: + QV8Engine *d; + Q_DISABLE_COPY(QJSEngine) + Q_DECLARE_PRIVATE(QJSEngine) + friend class QV8Engine; +}; + +inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) +{ + return QJSEngine::convertV2(value, type, ptr); +} + +template<typename T> +T qjsvalue_cast(const QJSValue &value) +{ + T t; + const int id = qMetaTypeId<T>(); + + if (qjsvalue_cast_helper(value, id, &t)) + return t; + else if (value.isVariant()) + return qvariant_cast<T>(value.toVariant()); + + return T(); +} + +template <> +inline QVariant qjsvalue_cast<QVariant>(const QJSValue &value) +{ + return value.toVariant(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QJSENGINE_H diff --git a/src/qml/qml/v8/qjsengine_p.h b/src/qml/qml/v8/qjsengine_p.h new file mode 100644 index 0000000000..ecd5f7cc86 --- /dev/null +++ b/src/qml/qml/v8/qjsengine_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_P_H +#define QJSENGINE_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/private/qobject_p.h> +#include "qjsengine.h" + + +QT_BEGIN_NAMESPACE + + +class QJSEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QJSEngine) + +public: + static QJSEnginePrivate* get(QJSEngine*e) { return e->d_func(); } + + QJSEnginePrivate() {} +}; + +QT_END_NAMESPACE + +#endif // QJSENGINE_P_H diff --git a/src/qml/qml/v8/qjsvalue.cpp b/src/qml/qml/v8/qjsvalue.cpp new file mode 100644 index 0000000000..e0a925c3bb --- /dev/null +++ b/src/qml/qml/v8/qjsvalue.cpp @@ -0,0 +1,856 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptisolate_p.h" +#include "qjsengine.h" +#include "qv8engine_p.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscript_impl_p.h" +#include "qscriptshareddata_p.h" +#include <QtCore/qstring.h> + +/*! + \since 5.0 + \class QJSValue + + \brief The QJSValue class acts as a container for Qt/JavaScript data types. + + \ingroup qtjavascript + \mainclass + + QJSValue supports the types defined in the \l{ECMA-262} + standard: The primitive types, which are Undefined, Null, Boolean, + Number, and String; and the Object type. Additionally, built-in + support is provided for Qt/C++ types such as QVariant and QObject. + + For the object-based types (including Date and RegExp), use the + newT() functions in QJSEngine (e.g. QJSEngine::newObject()) + to create a QJSValue of the desired type. For the primitive types, + use one of the QJSValue constructor overloads. + + The methods named isT() (e.g. isBool(), isUndefined()) can be + used to test if a value is of a certain type. The methods named + toT() (e.g. toBool(), toString()) can be used to convert a + QJSValue to another type. You can also use the generic + QJSValue_cast() function. + + Object values have zero or more properties which are themselves + QJSValues. Use setProperty() to set a property of an object, and + call property() to retrieve the value of a property. + + \snippet doc/src/snippets/code/src_script_qjsvalue.cpp 0 + + If you want to iterate over the properties of a script object, use + the QJSValueIterator class. + + Object values have an internal \c{prototype} property, which can be + accessed with prototype() and setPrototype(). + + Function objects (objects for which isCallable()) returns true) can + be invoked by calling call(). Constructor functions can be used to + construct new objects by calling callAsConstructor(). + + Use equals() or strictlyEquals() to compare a QJSValue to another. + + Note that a QJSValue for which isObject() is true only carries a + reference to an actual object; copying the QJSValue will only + copy the object reference, not the object itself. If you want to + clone an object (i.e. copy an object's properties to another + object), you can do so with the help of a \c{for-in} statement in + script code, or QJSValueIterator in C++. + + \sa QJSEngine, QJSValueIterator +*/ + +/*! + \enum QJSValue::SpecialValue + + This enum is used to specify a single-valued type. + + \value UndefinedValue An undefined value. + + \value NullValue A null value. +*/ + +QT_BEGIN_NAMESPACE + +/*! + Constructs a new QJSValue with a boolean \a value. +*/ +QJSValue::QJSValue(bool value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(int value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(uint value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(double value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QString& value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a special \a value. +*/ +QJSValue::QJSValue(SpecialValue value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QLatin1String &value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +#ifndef QT_NO_CAST_FROM_ASCII +QJSValue::QJSValue(const char *value) + : d_ptr(new QJSValuePrivate(QString::fromAscii(value))) +{ +} +#endif + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QJSValuePrivate* d) + : d_ptr(d) +{ +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QScriptPassPointer<QJSValuePrivate> d) + : d_ptr(d.give()) +{ +} + +/*! + Constructs a new QJSValue that is a copy of \a other. + + Note that if \a other is an object (i.e., isObject() would return + true), then only a reference to the underlying object is copied into + the new script value (i.e., the object itself is not copied). +*/ +QJSValue::QJSValue(const QJSValue& other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys this QJSValue. +*/ +QJSValue::~QJSValue() +{ +} + +/*! + Returns true if this QJSValue is of the primitive type Boolean; + otherwise returns false. + + \sa toBool() +*/ +bool QJSValue::isBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isBool(); +} + +/*! + Returns true if this QJSValue is of the primitive type Number; + otherwise returns false. + + \sa toNumber() +*/ +bool QJSValue::isNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNumber(); +} + +/*! + Returns true if this QJSValue is of the primitive type Null; + otherwise returns false. +*/ +bool QJSValue::isNull() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNull(); +} + +/*! + Returns true if this QJSValue is of the primitive type String; + otherwise returns false. + + \sa toString() +*/ +bool QJSValue::isString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isString(); +} + +/*! + Returns true if this QJSValue is of the primitive type Undefined; + otherwise returns false. +*/ +bool QJSValue::isUndefined() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isUndefined(); +} + +/*! + Returns true if this QJSValue is an object of the Error class; + otherwise returns false. +*/ +bool QJSValue::isError() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isError(); +} + +/*! + Returns true if this QJSValue is an object of the Array class; + otherwise returns false. + + \sa QJSEngine::newArray() +*/ +bool QJSValue::isArray() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isArray(); + } + +/*! + Returns true if this QJSValue is of the Object type; otherwise + returns false. + + Note that function values, variant values, and QObject values are + objects, so this function returns true for such values. + + \sa QJSEngine::newObject() +*/ +bool QJSValue::isObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isObject(); +} + +/*! + Returns true if this QJSValue can be called a function, otherwise + returns false. + + \sa call() +*/ +bool QJSValue::isCallable() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isCallable(); +} + +/*! + Returns true if this QJSValue is a variant value; + otherwise returns false. + + \sa toVariant() +*/ +bool QJSValue::isVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isVariant(); +} + +/*! + Returns the string value of this QJSValue, as defined in + \l{ECMA-262} section 9.8, "ToString". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's toString() function (and possibly valueOf()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isString() +*/ +QString QJSValue::toString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toString(); +} + +/*! + Returns the number value of this QJSValue, as defined in + \l{ECMA-262} section 9.3, "ToNumber". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isNumber(), toInt(), toUInt() +*/ +double QJSValue::toNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toNumber(); +} + +/*! + Returns the boolean value of this QJSValue, using the conversion + rules described in \l{ECMA-262} section 9.2, "ToBoolean". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isBool() +*/ +bool QJSValue::toBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toBool(); +} + +/*! + Returns the signed 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.5, "ToInt32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toUInt() +*/ +qint32 QJSValue::toInt() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toInt32(); +} + +/*! + Returns the unsigned 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.6, "ToUint32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toInt() +*/ +quint32 QJSValue::toUInt() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt32(); +} + +/*! + Returns the QVariant value of this QJSValue, if it can be + converted to a QVariant; otherwise returns an invalid QVariant. + The conversion is performed according to the following table: + + \table + \header \o Input Type \o Result + \row \o Undefined \o An invalid QVariant. + \row \o Null \o An invalid QVariant. + \row \o Boolean \o A QVariant containing the value of the boolean. + \row \o Number \o A QVariant containing the value of the number. + \row \o String \o A QVariant containing the value of the string. + \row \o QVariant Object \o The result is the QVariant value of the object (no conversion). + \row \o QObject Object \o A QVariant containing a pointer to the QObject. + \row \o Date Object \o A QVariant containing the date value (toDateTime()). + \row \o RegExp Object \o A QVariant containing the regular expression value. + \row \o Array Object \o The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed. + \row \o Object \o The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed. + \endtable + + \sa isVariant() +*/ +QVariant QJSValue::toVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toVariant(); +} + +/*! + Calls this QJSValue as a function, passing \a args as arguments + to the function, and using the globalObject() as the "this"-object. + Returns the value returned from the function. + + If this QJSValue is not callable, call() does nothing and + returns an undefined QJSValue. + + Calling call() can cause an exception to occur in the script engine; + in that case, call() returns the value that was thrown (typically an + \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa isCallable(), callWithInstance(), callAsConstructor() +*/ +QJSValue QJSValue::call(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(/*thisObject=*/0, args); +} + +/*! + Calls this QJSValue as a function, using \a instance as + the `this' object in the function call, and passing \a args + as arguments to the function. Returns the value returned from + the function. + + If this QJSValue is not a function, call() does nothing + and returns an undefined QJSValue. + + Note that if \a instance is not an object, the global object + (see \l{QJSEngine::globalObject()}) will be used as the + `this' object. + + Calling call() can cause an exception to occur in the script engine; + in that case, call() returns the value that was thrown (typically an + \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call() +*/ +QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(QJSValuePrivate::get(instance), args); +} + +/*! + Creates a new \c{Object} and calls this QJSValue as a + constructor, using the created object as the `this' object and + passing \a args as arguments. If the return value from the + constructor call is an object, then that object is returned; + otherwise the default constructed object is returned. + + If this QJSValue is not a function, callAsConstructor() does + nothing and returns an undefined QJSValue. + + Calling this function can cause an exception to occur in the + script engine; in that case, the value that was thrown + (typically an \c{Error} object) is returned. You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call(), QJSEngine::newObject() +*/ +QJSValue QJSValue::callAsConstructor(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->callAsConstructor(args)); +} + +#ifdef QT_DEPRECATED + +/*! + \obsolete + + Returns the QJSEngine that created this QJSValue, + or 0 if this QJSValue is invalid or the value is not + associated with a particular engine. +*/ +QJSEngine* QJSValue::engine() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + QV8Engine* engine = d->engine(); + if (engine) + return QV8Engine::get(engine); + return 0; +} + +#endif // QT_DEPRECATED + +/*! + If this QJSValue is an object, returns the internal prototype + (\c{__proto__} property) of this object; otherwise returns an + undefined QJSValue. + + \sa setPrototype(), isObject() +*/ +QJSValue QJSValue::prototype() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->prototype()); +} + +/*! + If this QJSValue is an object, sets the internal prototype + (\c{__proto__} property) of this object to be \a prototype; + otherwise does nothing. + + The internal prototype should not be confused with the public + property with name "prototype"; the public prototype is usually + only set on functions that act as constructors. + + \sa prototype(), isObject() +*/ +void QJSValue::setPrototype(const QJSValue& prototype) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setPrototype(QJSValuePrivate::get(prototype)); +} + +/*! + Assigns the \a other value to this QJSValue. + + Note that if \a other is an object (isObject() returns true), + only a reference to the underlying object will be assigned; + the object itself will not be copied. +*/ +QJSValue& QJSValue::operator=(const QJSValue& other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if this QJSValue is equal to \a other, otherwise + returns false. The comparison follows the behavior described in + \l{ECMA-262} section 11.9.3, "The Abstract Equality Comparison + Algorithm". + + This function can return true even if the type of this QJSValue + is different from the type of the \a other value; i.e. the + comparison is not strict. For example, comparing the number 9 to + the string "9" returns true; comparing an undefined value to a null + value returns true; comparing a \c{Number} object whose primitive + value is 6 to a \c{String} object whose primitive value is "6" + returns true; and comparing the number 1 to the boolean value + \c{true} returns true. If you want to perform a comparison + without such implicit value conversion, use strictlyEquals(). + + Note that if this QJSValue or the \a other value are objects, + calling this function has side effects on the script engine, since + the engine will call the object's valueOf() function (and possibly + toString()) in an attempt to convert the object to a primitive value + (possibly resulting in an uncaught script exception). + + \sa strictlyEquals() +*/ +bool QJSValue::equals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* otherValue = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : otherValue->engine()); + return d_ptr->equals(otherValue); +} + +/*! + Returns true if this QJSValue is equal to \a other using strict + comparison (no conversion), otherwise returns false. The comparison + follows the behavior described in \l{ECMA-262} section 11.9.6, "The + Strict Equality Comparison Algorithm". + + If the type of this QJSValue is different from the type of the + \a other value, this function returns false. If the types are equal, + the result depends on the type, as shown in the following table: + + \table + \header \o Type \o Result + \row \o Undefined \o true + \row \o Null \o true + \row \o Boolean \o true if both values are true, false otherwise + \row \o Number \o false if either value is NaN (Not-a-Number); true if values are equal, false otherwise + \row \o String \o true if both values are exactly the same sequence of characters, false otherwise + \row \o Object \o true if both values refer to the same object, false otherwise + \endtable + + \sa equals() +*/ +bool QJSValue::strictlyEquals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* o = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : o->engine()); + return d_ptr->strictlyEquals(o); +} + +/*! + Returns the value of this QJSValue's property with the given \a name. + If no such property exists, an undefined QJSValue is returned. + + If the property is implemented using a getter function (i.e. has the + PropertyGetter flag set), calling property() has side-effects on the + script engine, since the getter function will be called (possibly + resulting in an uncaught script exception). If an exception + occurred, property() returns the value that was thrown (typically + an \c{Error} object). + + \sa setProperty(), hasProperty(), QJSValueIterator +*/ +QJSValue QJSValue::property(const QString& name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(name)); +} + +/*! + \overload + + Returns the property at the given \a arrayIndex. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if property() was called with the string representation of \a + arrayIndex. +*/ +QJSValue QJSValue::property(quint32 arrayIndex) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(arrayIndex)); +} + +/*! + Sets the value of this QJSValue's property with the given \a name to + the given \a value. + + If this QJSValue is not an object, this function does nothing. + + If this QJSValue does not already have a property with name \a name, + a new property is created. + + \sa property(), deleteProperty() +*/ +void QJSValue::setProperty(const QString& name, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(name, QJSValuePrivate::get(value)); +} + +/*! + \overload + + Sets the property at the given \a arrayIndex to the given \a value. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if setProperty() was called with the string representation of \a + arrayIndex. +*/ +void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(arrayIndex, QJSValuePrivate::get(value)); +} + +/*! + Attempts to delete this object's property of the given \a name. + Returns true if the property was deleted, otherwise returns false. + + The behavior of this function is consistent with the JavaScript + delete operator. In particular: + + \list + \o Non-configurable properties cannot be deleted. + \o This function will return true even if this object doesn't + have a property of the given \a name (i.e., non-existent + properties are "trivially deletable"). + \o If this object doesn't have an own property of the given + \a name, but an object in the prototype() chain does, the + prototype object's property is not deleted, and this function + returns true. + \endlist + + \sa setProperty(), hasOwnProperty() +*/ +bool QJSValue::deleteProperty(const QString &name) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->deleteProperty(name); +} + +/*! + Returns true if this object has a property of the given \a name, + otherwise returns false. + + \sa property(), hasOwnProperty() +*/ +bool QJSValue::hasProperty(const QString &name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->hasProperty(name); +} + +/*! + Returns true if this object has an own (not prototype-inherited) + property of the given \a name, otherwise returns false. + + \sa property(), hasProperty() +*/ +bool QJSValue::hasOwnProperty(const QString &name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->hasOwnProperty(name); +} + +/*! + * If this QJSValue is a QObject, returns the QObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * If the QObject that this QJSValue wraps has been deleted, + * this function returns 0 (i.e. it is possible for toQObject() + * to return 0 even when isQObject() returns true). + * + * \sa isQObject() + */ +QObject *QJSValue::toQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toQObject(); +} + +/*! + Returns a QDateTime representation of this value, in local time. + If this QJSValue is not a date, or the value of the date is NaN + (Not-a-Number), an invalid QDateTime is returned. + + \sa isDate() +*/ +QDateTime QJSValue::toDateTime() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toDataTime(); +} + +/*! + Returns true if this QJSValue is an object of the Date class; + otherwise returns false. + + \sa QJSEngine::newDate() +*/ +bool QJSValue::isDate() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isDate(); +} + +/*! + Returns true if this QJSValue is an object of the RegExp class; + otherwise returns false. +*/ +bool QJSValue::isRegExp() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isRegExp(); +} + +/*! + Returns true if this QJSValue is a QObject; otherwise returns + false. + + Note: This function returns true even if the QObject that this + QJSValue wraps has been deleted. + + \sa toQObject(), QJSEngine::newQObject() +*/ +bool QJSValue::isQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isQObject(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qjsvalue.h b/src/qml/qml/v8/qjsvalue.h new file mode 100644 index 0000000000..30ea2e7345 --- /dev/null +++ b/src/qml/qml/v8/qjsvalue.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUE_H +#define QJSVALUE_H + +#include <QtQml/qtqmlglobal.h> + +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QJSValue; +class QJSEngine; +class QVariant; +class QObject; +struct QMetaObject; +class QDateTime; + +typedef QList<QJSValue> QJSValueList; + +class QJSValuePrivate; +struct QScriptValuePrivatePointerDeleter; +template <class T> class QScriptPassPointer; + +class Q_QML_EXPORT QJSValue +{ +public: + enum SpecialValue { + NullValue, + UndefinedValue + }; + +public: + QJSValue(SpecialValue value = UndefinedValue); + ~QJSValue(); + QJSValue(const QJSValue &other); + + QJSValue(bool value); + QJSValue(int value); + QJSValue(uint value); + QJSValue(double value); + QJSValue(const QString &value); + QJSValue(const QLatin1String &value); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QJSValue(const char *str); +#endif + + QJSValue &operator=(const QJSValue &other); + + bool isBool() const; + bool isNumber() const; + bool isNull() const; + bool isString() const; + bool isUndefined() const; + bool isVariant() const; + bool isQObject() const; + bool isObject() const; + bool isDate() const; + bool isRegExp() const; + bool isArray() const; + bool isError() const; + + QString toString() const; + double toNumber() const; + qint32 toInt() const; + quint32 toUInt() const; + bool toBool() const; + QVariant toVariant() const; + QObject *toQObject() const; + QDateTime toDateTime() const; + + bool equals(const QJSValue &other) const; + bool strictlyEquals(const QJSValue &other) const; + + QJSValue prototype() const; + void setPrototype(const QJSValue &prototype); + + QJSValue property(const QString &name) const; + void setProperty(const QString &name, const QJSValue &value); + + bool hasProperty(const QString &name) const; + bool hasOwnProperty(const QString &name) const; + + QJSValue property(quint32 arrayIndex) const; + void setProperty(quint32 arrayIndex, const QJSValue &value); + + bool deleteProperty(const QString &name); + + bool isCallable() const; + QJSValue call(const QJSValueList &args = QJSValueList()); + QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); + QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); + +#ifdef QT_DEPRECATED + QT_DEPRECATED QJSEngine *engine() const; +#endif + +private: + // force compile error, prevent QJSValue(bool) to be called + + QJSValue(void *) Q_DECL_EQ_DELETE; + + QJSValue(QJSValuePrivate*); + QJSValue(QScriptPassPointer<QJSValuePrivate>); + +private: + QExplicitlySharedDataPointer<QJSValuePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValue) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/qml/qml/v8/qjsvalue_impl_p.h b/src/qml/qml/v8/qjsvalue_impl_p.h new file mode 100644 index 0000000000..cd33859c50 --- /dev/null +++ b/src/qml/qml/v8/qjsvalue_impl_p.h @@ -0,0 +1,977 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QJSVALUE_IMPL_P_H +#define QJSVALUE_IMPL_P_H + +#include "qjsconverter_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscriptisolate_p.h" + +QT_BEGIN_NAMESPACE + +QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); } + +QJSValue QJSValuePrivate::get(const QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(const_cast<QJSValuePrivate*>(d)); +} + +QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +QJSValue QJSValuePrivate::get(QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +QJSValuePrivate::QJSValuePrivate(bool value) + : m_engine(0), m_state(CBool), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(int value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(uint value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(double value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(const QString& value) + : m_engine(0), m_state(CString), u(new QString(value)) +{ +} + +QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value) + : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined) +{ +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value) + : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::New(value)) +{ + Q_ASSERT(engine); + // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug + // information and it can't be simply ignored. + Q_ASSERT(!value.IsEmpty()); + m_engine->registerValue(this); +} + +QJSValuePrivate::~QJSValuePrivate() +{ + if (isJSBased()) { + m_engine->unregisterValue(this); + QScriptIsolate api(m_engine); + m_value.Dispose(); + } else if (isStringBased()) { + delete u.m_string; + } +} + +bool QJSValuePrivate::toBool() const +{ + switch (m_state) { + case JSValue: + { + v8::HandleScope scope; + return m_value->ToBoolean()->Value(); + } + case CNumber: + return !(qIsNaN(u.m_number) || !u.m_number); + case CBool: + return u.m_bool; + case CNull: + case CUndefined: + return false; + case CString: + return u.m_string->length(); + } + + Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); + return false; // Avoid compiler warning. +} + +double QJSValuePrivate::toNumber() const +{ + switch (m_state) { + case JSValue: + { + v8::HandleScope scope; + return m_value->ToNumber()->Value(); + } + case CNumber: + return u.m_number; + case CBool: + return u.m_bool ? 1 : 0; + case CNull: + case CUndefined: + return qQNaN(); + case CString: + bool ok; + double result = u.m_string->toDouble(&ok); + if (ok) + return result; + result = u.m_string->toInt(&ok, 0); // Try other bases. + if (ok) + return result; + if (*u.m_string == QLatin1String("Infinity")) + return qInf(); + if (*u.m_string == QLatin1String("-Infinity")) + return -qInf(); + return u.m_string->length() ? qQNaN() : 0; + } + + Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); + return 0; // Avoid compiler warning. +} + +QString QJSValuePrivate::toString() const +{ + switch (m_state) { + case CBool: + return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false"); + case CString: + return *u.m_string; + case CNumber: + return QJSConverter::toString(u.m_number); + case CNull: + return QString::fromLatin1("null"); + case CUndefined: + return QString::fromLatin1("undefined"); + case JSValue: + Q_ASSERT(!m_value.IsEmpty()); + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Local<v8::String> result = m_value->ToString(); + if (result.IsEmpty()) { + result = tryCatch.Exception()->ToString(); + m_engine->setException(tryCatch.Exception(), tryCatch.Message()); + } + return QJSConverter::toString(result); + } + + Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); + return QString(); // Avoid compiler warning. +} + +QVariant QJSValuePrivate::toVariant() const +{ + switch (m_state) { + case CBool: + return QVariant(u.m_bool); + case CString: + return QVariant(*u.m_string); + case CNumber: + return QVariant(u.m_number); + case CNull: + return QVariant(); + case CUndefined: + return QVariant(); + case JSValue: + break; + } + + Q_ASSERT(m_state == JSValue); + Q_ASSERT(!m_value.IsEmpty()); + Q_ASSERT(m_engine); + + v8::HandleScope handleScope; + return m_engine->variantFromJS(m_value); +} + +inline QDateTime QJSValuePrivate::toDataTime() const +{ + if (!isDate()) + return QDateTime(); + + v8::HandleScope handleScope; + return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value)); + +} + +QObject* QJSValuePrivate::toQObject() const +{ + if (!isJSBased()) + return 0; + + v8::HandleScope handleScope; + return engine()->qtObjectFromJS(m_value); +} + +double QJSValuePrivate::toInteger() const +{ + double result = toNumber(); + if (qIsNaN(result)) + return 0; + if (qIsInf(result)) + return result; + + // Must use floor explicitly rather than qFloor here. On some + // platforms qFloor will cast the value to a single precision float and use + // floorf() which results in test failures. + return (result > 0) ? floor(result) : -1 * floor(-result); +} + +qint32 QJSValuePrivate::toInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + return result; +} + +quint32 QJSValuePrivate::toUInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + + // The explicit casts are required to avoid undefined behaviour. For example, casting + // a negative double directly to an unsigned int on ARM NEON FPU results in the value + // being set to zero. Casting to a signed int first ensures well defined behaviour. + return (quint32) (qint32) result; +} + +quint16 QJSValuePrivate::toUInt16() const +{ + return toInt32(); +} + +inline bool QJSValuePrivate::isArray() const +{ + return isJSBased() && m_value->IsArray(); +} + +inline bool QJSValuePrivate::isBool() const +{ + return m_state == CBool || (isJSBased() && m_value->IsBoolean()); +} + +inline bool QJSValuePrivate::isCallable() const +{ + if (isFunction()) + return true; + if (isObject()) { + // Our C++ wrappers register function handlers but not always act as callables. + return v8::Object::Cast(*m_value)->IsCallable(); + } + return false; +} + +inline bool QJSValuePrivate::isError() const +{ + if (!isJSBased()) + return false; + v8::HandleScope handleScope; + return m_value->IsError(); +} + +inline bool QJSValuePrivate::isFunction() const +{ + return isJSBased() && m_value->IsFunction(); +} + +inline bool QJSValuePrivate::isNull() const +{ + return m_state == CNull || (isJSBased() && m_value->IsNull()); +} + +inline bool QJSValuePrivate::isNumber() const +{ + return m_state == CNumber || (isJSBased() && m_value->IsNumber()); +} + +inline bool QJSValuePrivate::isObject() const +{ + return isJSBased() && m_value->IsObject(); +} + +inline bool QJSValuePrivate::isString() const +{ + return m_state == CString || (isJSBased() && m_value->IsString()); +} + +inline bool QJSValuePrivate::isUndefined() const +{ + return m_state == CUndefined || (isJSBased() && m_value->IsUndefined()); +} + +inline bool QJSValuePrivate::isVariant() const +{ + return isJSBased() && m_engine->isVariant(m_value); +} + +bool QJSValuePrivate::isDate() const +{ + return (isJSBased() && m_value->IsDate()); +} + +bool QJSValuePrivate::isRegExp() const +{ + return (isJSBased() && m_value->IsRegExp()); +} + +bool QJSValuePrivate::isQObject() const +{ + return isJSBased() && engine()->isQObject(m_value); +} + +inline bool QJSValuePrivate::equals(QJSValuePrivate* other) +{ + if (!isJSBased() && !other->isJSBased()) { + switch (m_state) { + case CNull: + case CUndefined: + return other->isUndefined() || other->isNull(); + case CNumber: + switch (other->m_state) { + case CBool: + case CString: + return u.m_number == other->toNumber(); + case CNumber: + return u.m_number == other->u.m_number; + default: + return false; + } + case CBool: + switch (other->m_state) { + case CBool: + return u.m_bool == other->u.m_bool; + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return toNumber() == other->toNumber(); + default: + return false; + } + case CString: + switch (other->m_state) { + case CBool: + return toNumber() == other->toNumber(); + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return *u.m_string == *other->u.m_string; + default: + return false; + } + default: + Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement."); + } + } + + v8::HandleScope handleScope; + if (isJSBased() && !other->isJSBased()) { + if (!other->assignEngine(engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } else if (!isJSBased() && other->isJSBased()) { + if (!assignEngine(other->engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } + + Q_ASSERT(this->engine() && other->engine()); + if (this->engine() != other->engine()) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->Equals(other->m_value); +} + +inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other) +{ + if (isJSBased()) { + // We can't compare these two values without binding to the same engine. + if (!other->isJSBased()) { + if (other->assignEngine(engine())) + return m_value->StrictEquals(other->m_value); + return false; + } + if (other->engine() != engine()) { + qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->StrictEquals(other->m_value); + } + if (isStringBased()) { + if (other->isStringBased()) + return *u.m_string == *(other->u.m_string); + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + } + if (isNumberBased()) { + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + if (m_state != other->m_state) + return false; + if (m_state == CNumber) + return u.m_number == other->u.m_number; + Q_ASSERT(m_state == CBool); + return u.m_bool == other->u.m_bool; + } + + return (isUndefined() && other->isUndefined()) + || (isNull() && other->isNull()); +} + +inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const +{ + if (engine() != other->engine() && engine() && other->engine()) { + qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine"); + return false; + } + + if (isString() && other->isString()) + return toString() < other->toString(); + + if (isObject() || other->isObject()) { + v8::HandleScope handleScope; + QV8Engine *eng = m_engine ? engine() : other->engine(); + // FIXME: lessThan can throw an exception which will be dropped by this code: + Q_ASSERT(eng); + eng->saveException(); + QScriptSharedDataPointer<QJSValuePrivate> cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return a<b})"))); + Q_ASSERT(cmp->isFunction()); + v8::Handle<v8::Value> args[2]; + cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other)); + QScriptSharedDataPointer<QJSValuePrivate> resultValue(cmp->call(0, 2, args)); + bool result = resultValue->toBool(); + eng->restoreException(); + return result; + } + + double nthis = toNumber(); + double nother = other->toNumber(); + if (qIsNaN(nthis) || qIsNaN(nother)) { + // Should return undefined in ECMA standard. + return false; + } + return nthis < nother; +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const +{ + if (isObject()) { + v8::HandleScope handleScope; + return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype()); + } + return new QJSValuePrivate(); +} + +inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype) +{ + if (isObject() && (prototype->isObject() || prototype->isNull())) { + if (engine() != prototype->engine()) { + if (prototype->engine()) { + qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); + return; + } + prototype->assignEngine(engine()); + } + v8::HandleScope handleScope; + if (!v8::Handle<v8::Object>::Cast(m_value)->SetPrototype(*prototype)) + qWarning("QJSValue::setPrototype() failed: cyclic prototype value"); + } +} + +inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs) +{ + if (!isObject()) + return; + v8::HandleScope handleScope; + setProperty(QJSConverter::toString(name), value, attribs); +} + +inline void QJSValuePrivate::setProperty(v8::Handle<v8::String> name, QJSValuePrivate* value, uint attribs) +{ + if (!isObject()) + return; + + if (!value->isJSBased()) + value->assignEngine(engine()); + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty(%s) failed: " + "cannot set value created in a different engine", + qPrintable(QJSConverter::toString(name))); + return; + } + + v8::TryCatch tryCatch; +// if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) { +// engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs); +// } else { + v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask)); +// } + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs) +{ + // FIXME this method should by integrated with other overloads to use the same code patch. + // for now it is not possible as v8 doesn't allow to set property attributes using index based api. + + if (!isObject()) + return; + + if (attribs) { + // FIXME we dont need to convert index to a string. + //Object::Set(int,value) do not take attributes. + setProperty(QString::number(index), value, attribs); + return; + } + + if (!value->isJSBased()) + value->assignEngine(engine()); + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine"); + return; + } + + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Object::Cast(*m_value)->Set(index, value->m_value); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const +{ + if (!isObject()) + return new QJSValuePrivate(); + if (!name.length()) + return new QJSValuePrivate(engine()); + + v8::HandleScope handleScope; + return property(QJSConverter::toString(name)); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const +{ + Q_ASSERT(!name.IsEmpty()); + if (!isObject()) + return new QJSValuePrivate(); + return property<>(name); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const +{ + if (!isObject()) + return new QJSValuePrivate(); + return property<>(index); +} + +template<typename T> +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const +{ + Q_ASSERT(isObject()); + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Object::Cast(*m_value)); + + v8::TryCatch tryCatch; + v8::Handle<v8::Value> result = self->Get(name); + if (tryCatch.HasCaught()) { + result = tryCatch.Exception(); + engine()->setException(result, tryCatch.Message()); + return new QJSValuePrivate(engine(), result); + } + if (result.IsEmpty()) + return new QJSValuePrivate(engine()); + return new QJSValuePrivate(engine(), result); +} + +inline bool QJSValuePrivate::deleteProperty(const QString& name) +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->Delete(QJSConverter::toString(name)); +} + +inline bool QJSValuePrivate::hasProperty(const QString &name) const +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->Has(QJSConverter::toString(name)); +} + +inline bool QJSValuePrivate::hasOwnProperty(const QString &name) const +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->HasOwnProperty(QJSConverter::toString(name)); +} + +inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const +{ + if (!isObject()) + return QJSValuePrivate::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name)); +} + +inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const +{ + if (!isObject()) + return QJSValuePrivate::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args) +{ + if (!isCallable()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); + return new QJSValuePrivate(engine()); + } + + return call(thisObject, argc, argv.data()); +} + +QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv) +{ + QV8Engine *e = engine(); + + v8::Handle<v8::Object> recv; + + if (!thisObject || !thisObject->isObject()) { + recv = v8::Handle<v8::Object>(v8::Object::Cast(*e->global())); + } else { + if (!thisObject->assignEngine(e)) { + qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine"); + return new QJSValuePrivate(engine()); + } + + recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value)); + } + + if (argc < 0) { + v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array")); + e->setException(exeption); + return new QJSValuePrivate(e, exeption); + } + + v8::TryCatch tryCatch; + v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv); + + if (result.IsEmpty()) { + result = tryCatch.Exception(); + // TODO: figure out why v8 doesn't always produce an exception value. + //Q_ASSERT(!result.IsEmpty()); + if (result.IsEmpty()) + result = v8::Exception::Error(v8::String::New("missing exception value")); + e->setException(result, tryCatch.Message()); + } + + return new QJSValuePrivate(e, result); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(int argc, v8::Handle<v8::Value> *argv) +{ + QV8Engine *e = engine(); + + if (argc < 0) { + v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array")); + e->setException(exeption); + return new QJSValuePrivate(e, exeption); + } + + v8::TryCatch tryCatch; + v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv); + + if (result.IsEmpty()) { + result = tryCatch.Exception(); + e->setException(result, tryCatch.Message()); + } + + return new QJSValuePrivate(e, result); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(const QJSValueList& args) +{ + if (!isCallable()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine"); + return new QJSValuePrivate(engine()); + } + + return callAsConstructor(argc, argv.data()); +} + +/*! \internal + * Make sure this value is associated with a v8 value belonging to this engine. + * If the value belongs to another engine, returns false. + */ +bool QJSValuePrivate::assignEngine(QV8Engine* engine) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + switch (m_state) { + case CBool: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool)); + break; + case CString: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string)); + delete u.m_string; + break; + case CNumber: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number)); + break; + case CNull: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue)); + break; + case CUndefined: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::UndefinedValue)); + break; + default: + if (this->engine() == engine) + return true; + else if (!isJSBased()) + Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); + else + qWarning("JSValue can't be rassigned to an another engine."); + return false; + } + m_engine = engine; + m_state = JSValue; + + m_engine->registerValue(this); + return true; +} + +/*! + \internal + Invalidates this value (makes it undefined). + + Does not remove the value from the engine's list of + registered values; that's the responsibility of the caller. +*/ +void QJSValuePrivate::invalidate() +{ + if (isJSBased()) { + m_value.Dispose(); + m_value.Clear(); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = 0; + m_state = CUndefined; +} + +QV8Engine* QJSValuePrivate::engine() const +{ + return m_engine; +} + +inline QJSValuePrivate::operator v8::Handle<v8::Value>() const +{ + Q_ASSERT(isJSBased()); + return m_value; +} + +inline QJSValuePrivate::operator v8::Handle<v8::Object>() const +{ + Q_ASSERT(isObject()); + return v8::Handle<v8::Object>::Cast(m_value); +} + +/*! + * Return a v8::Handle, assign to the engine if needed. + */ +v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine) +{ + if (!m_engine) { + if (!assignEngine(engine)) + return v8::Handle<v8::Value>(); + } + Q_ASSERT(isJSBased()); + return m_value; +} + +/*! + \internal + Returns true if QSV have an engine associated. +*/ +bool QJSValuePrivate::isJSBased() const +{ +#ifndef QT_NO_DEBUG + // internals check. + if (m_state >= JSValue) + Q_ASSERT(!m_value.IsEmpty()); + else + Q_ASSERT(m_value.IsEmpty()); +#endif + return m_state >= JSValue; +} + +/*! + \internal + Returns true if current value of QSV is placed in m_number. +*/ +bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; } + +/*! + \internal + Returns true if current value of QSV is placed in m_string. +*/ +bool QJSValuePrivate::isStringBased() const { return m_state == CString; } + +/*! + \internal + Converts arguments and bind them to the engine. + \attention argv should be big enough +*/ +inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& args) const +{ + QJSValueList::const_iterator i = args.constBegin(); + for (int j = 0; i != args.constEnd(); j++, i++) { + QJSValuePrivate* value = QJSValuePrivate::get(*i); + if ((value->isJSBased() && engine() != value->engine()) + || (!value->isJSBased() && !value->assignEngine(engine()))) + // Different engines are not allowed! + return false; + argv[j] = *value; + } + return true; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsvalue_p.h b/src/qml/qml/v8/qjsvalue_p.h new file mode 100644 index 0000000000..3eccba64bd --- /dev/null +++ b/src/qml/qml/v8/qjsvalue_p.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QJSVALUE_P_H +#define QJSVALUE_P_H + +#include <private/qv8_p.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> +#include <qdebug.h> + +#include <private/qintrusivelist_p.h> +#include "qscriptshareddata_p.h" +#include "qjsvalue.h" + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +/*! + \internal + \class QJSValuePrivate +*/ +class QJSValuePrivate + : public QSharedData +{ +public: + enum PropertyFlag { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004 + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) + + inline static QJSValuePrivate* get(const QJSValue& q); + inline static QJSValue get(const QJSValuePrivate* d); + inline static QJSValue get(QJSValuePrivate* d); + inline static QJSValue get(QScriptPassPointer<QJSValuePrivate> d); + inline ~QJSValuePrivate(); + + inline QJSValuePrivate(bool value); + inline QJSValuePrivate(int value); + inline QJSValuePrivate(uint value); + inline QJSValuePrivate(double value); + inline QJSValuePrivate(const QString& value); + inline QJSValuePrivate(QJSValue::SpecialValue value = QJSValue::UndefinedValue); + + inline QJSValuePrivate(QV8Engine *engine, bool value); + inline QJSValuePrivate(QV8Engine *engine, int value); + inline QJSValuePrivate(QV8Engine *engine, uint value); + inline QJSValuePrivate(QV8Engine *engine, double value); + inline QJSValuePrivate(QV8Engine *engine, const QString& value); + inline QJSValuePrivate(QV8Engine *engine, QJSValue::SpecialValue value = QJSValue::UndefinedValue); + inline QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value>); + inline void invalidate(); + + inline bool toBool() const; + inline double toNumber() const; + inline QString toString() const; + inline double toInteger() const; + inline qint32 toInt32() const; + inline quint32 toUInt32() const; + inline quint16 toUInt16() const; + inline QDateTime toDataTime() const; + inline QObject *toQObject() const; + inline QVariant toVariant() const; + + inline bool isArray() const; + inline bool isBool() const; + inline bool isCallable() const; + inline bool isError() const; + inline bool isFunction() const; + inline bool isNull() const; + inline bool isNumber() const; + inline bool isObject() const; + inline bool isString() const; + inline bool isUndefined() const; + inline bool isVariant() const; + inline bool isDate() const; + inline bool isRegExp() const; + inline bool isQObject() const; + + inline bool equals(QJSValuePrivate* other); + inline bool strictlyEquals(QJSValuePrivate* other); + inline bool lessThan(QJSValuePrivate *other) const; + + inline QScriptPassPointer<QJSValuePrivate> prototype() const; + inline void setPrototype(QJSValuePrivate* prototype); + + inline void setProperty(const QString &name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(v8::Handle<v8::String> name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(quint32 index, QJSValuePrivate* value, uint attribs = 0); + inline QScriptPassPointer<QJSValuePrivate> property(const QString& name) const; + inline QScriptPassPointer<QJSValuePrivate> property(v8::Handle<v8::String> name) const; + inline QScriptPassPointer<QJSValuePrivate> property(quint32 index) const; + template<typename T> + inline QScriptPassPointer<QJSValuePrivate> property(T name) const; + inline bool deleteProperty(const QString& name); + inline bool hasProperty(const QString &name) const; + inline bool hasOwnProperty(const QString &name) const; + inline PropertyFlags propertyFlags(const QString& name) const; + inline PropertyFlags propertyFlags(v8::Handle<v8::String> name) const; + + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValueList& args); + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValue& arguments); + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, int argc, v8::Handle< v8::Value >* argv); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(int argc, v8::Handle<v8::Value> *argv); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValueList& args); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValue& arguments); + + inline bool assignEngine(QV8Engine *engine); + inline QV8Engine *engine() const; + + inline operator v8::Handle<v8::Value>() const; + inline operator v8::Handle<v8::Object>() const; + inline v8::Handle<v8::Value> asV8Value(QV8Engine *engine); +private: + QIntrusiveListNode m_node; + QV8Engine *m_engine; + + // Please, update class documentation when you change the enum. + enum State { + CString = 0x1000, + CNumber, + CBool, + CNull, + CUndefined, + JSValue = 0x2000, // V8 values are equal or higher then this value. + // JSPrimitive, + // JSObject + } m_state; + + union CValue { + bool m_bool; + double m_number; + QString* m_string; + + CValue() : m_number(0) {} + CValue(bool value) : m_bool(value) {} + CValue(int number) : m_number(number) {} + CValue(uint number) : m_number(number) {} + CValue(double number) : m_number(number) {} + CValue(QString* string) : m_string(string) {} + } u; + // v8::Persistent is not a POD, so can't be part of the union. + v8::Persistent<v8::Value> m_value; + + Q_DISABLE_COPY(QJSValuePrivate) + inline bool isJSBased() const; + inline bool isNumberBased() const; + inline bool isStringBased() const; + inline bool prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& arguments) const; + + friend class QV8Engine; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QJSValuePrivate::PropertyFlags) + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsvalueiterator.cpp b/src/qml/qml/v8/qjsvalueiterator.cpp new file mode 100644 index 0000000000..4c3fa15fd3 --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsvalueiterator.h" +#include "qjsvalueiterator_p.h" + +#include "qscriptisolate_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscript_impl_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJSValueIterator + + \brief The QJSValueIterator class provides a Java-style iterator for QJSValue. + + \ingroup qtjavascript + + + The QJSValueIterator constructor takes a QJSValue as + argument. After construction, the iterator is located at the very + beginning of the sequence of properties. Here's how to iterate over + all the properties of a QJSValue: + + \snippet doc/src/snippets/code/src_script_qjsvalueiterator.cpp 0 + + The next() advances the iterator. The name() and value() + functions return the name and value of the last item that was + jumped over. + + Note that QJSValueIterator only iterates over the QJSValue's + own properties; i.e. it does not follow the prototype chain. You can + use a loop like this to follow the prototype chain: + + \snippet doc/src/snippets/code/src_script_qjsvalueiterator.cpp 1 + + Note that QJSValueIterator will not automatically skip over + properties that have the QJSValue::SkipInEnumeration flag set; + that flag only affects iteration in script code. If you want, you + can skip over such properties with code like the following: + + \snippet doc/src/snippets/code/src_script_qjsvalueiterator.cpp 2 + + \sa QJSValue::property() +*/ + +/*! + Constructs an iterator for traversing \a object. The iterator is + set to be at the front of the sequence of properties (before the + first property). +*/ +QJSValueIterator::QJSValueIterator(const QJSValue& object) + : d_ptr(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))) +{} + +/*! + Destroys the iterator. +*/ +QJSValueIterator::~QJSValueIterator() +{} + +/*! + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next() +*/ +bool QJSValueIterator::hasNext() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->hasNext(); +} + +/*! + Advances the iterator by one position. + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), name() +*/ +bool QJSValueIterator::next() +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->next(); +} + +/*! + Returns the name of the last property that was jumped over using + next(). + + \sa value() +*/ +QString QJSValueIterator::name() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d_ptr->name(); +} + + +/*! + Returns the value of the last property that was jumped over using + next(). + + \sa name() +*/ +QJSValue QJSValueIterator::value() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->value()); +} + + +/*! + Makes the iterator operate on \a object. The iterator is set to be + at the front of the sequence of properties (before the first + property). +*/ +QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + d_ptr.reset(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))); + return *this; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qjsvalueiterator.h b/src/qml/qml/v8/qjsvalueiterator.h new file mode 100644 index 0000000000..c47f07d43b --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTVALUEITERATOR_H +#define QSCRIPTVALUEITERATOR_H + +#include <QtQml/qjsvalue.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QString; + +class QJSValueIteratorPrivate; +class Q_QML_EXPORT QJSValueIterator +{ +public: + QJSValueIterator(const QJSValue &value); + ~QJSValueIterator(); + + bool hasNext() const; + bool next(); + + QString name() const; + + QJSValue value() const; + QJSValueIterator& operator=(QJSValue &value); + +private: + QScopedPointer<QJSValueIteratorPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValueIterator) + Q_DISABLE_COPY(QJSValueIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTVALUEITERATOR_H diff --git a/src/qml/qml/v8/qjsvalueiterator_impl_p.h b/src/qml/qml/v8/qjsvalueiterator_impl_p.h new file mode 100644 index 0000000000..131296ecac --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator_impl_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUEITERATOR_IMPL_P_H +#define QJSVALUEITERATOR_IMPL_P_H + +#include "qjsvalueiterator_p.h" +#include <private/qv8engine_p.h> +#include "qjsconverter_p.h" + +inline QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValuePrivate* value) + : m_object(const_cast<QJSValuePrivate*>(value)) + , m_index(0) + , m_count(0) +{ + Q_ASSERT(value); + QV8Engine *engine = m_object->engine(); + if (!m_object->isObject()) + m_object = 0; + else { + QScriptIsolate api(engine, QScriptIsolate::NotNullEngine); + v8::HandleScope scope; + + v8::Handle<v8::Value> tmp = *value; + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(tmp); + v8::Local<v8::Array> names; + + // FIXME we need newer V8! + //names = obj->GetOwnPropertyNames(); + names = engine->getOwnPropertyNames(obj); + m_names = v8::Persistent<v8::Array>::New(names); + m_count = names->Length(); + + engine->registerValueIterator(this); + } +} + +inline QJSValueIteratorPrivate::~QJSValueIteratorPrivate() +{ + if (isValid()) { + engine()->unregisterValueIterator(this); + m_names.Dispose(); + } +} + +inline void QJSValueIteratorPrivate::invalidate() +{ + m_names.Dispose(); + m_object.reset(); + m_index = 0; + m_count = 0; +} + +inline bool QJSValueIteratorPrivate::hasNext() const +{ + return isValid() ? m_index < m_count : false; +} + +inline bool QJSValueIteratorPrivate::next() +{ + if (hasNext()) { + ++m_index; + return true; + } + return false; +} + +inline QString QJSValueIteratorPrivate::name() const +{ + if (!isValid()) + return QString(); + + v8::HandleScope handleScope; + return QJSConverter::toString(m_names->Get(m_index - 1)->ToString()); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValueIteratorPrivate::value() const +{ + if (!isValid()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + return m_object->property(m_names->Get(m_index - 1)->ToString()); +} + +inline bool QJSValueIteratorPrivate::isValid() const +{ + bool result = m_object ? !m_object->isUndefined() : false; + // We know that if this object is still valid then it is an object + // if this assumption is not correct then some other logic in this class + // have to be changed too. + Q_ASSERT(!result || m_object->isObject()); + return result; +} + +inline QV8Engine* QJSValueIteratorPrivate::engine() const +{ + return m_object ? m_object->engine() : 0; +} + +#endif // QJSVALUEITERATOR_IMPL_P_H diff --git a/src/qml/qml/v8/qjsvalueiterator_p.h b/src/qml/qml/v8/qjsvalueiterator_p.h new file mode 100644 index 0000000000..2a5bcdec22 --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUEITERATOR_P_H +#define QJSVALUEITERATOR_P_H + +#include <private/qintrusivelist_p.h> +#include "qjsvalue_p.h" + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +class QJSValueIteratorPrivate +{ +public: + inline QJSValueIteratorPrivate(const QJSValuePrivate* value); + inline ~QJSValueIteratorPrivate(); + + inline bool hasNext() const; + inline bool next(); + + inline QString name() const; + + inline QScriptPassPointer<QJSValuePrivate> value() const; + + inline bool isValid() const; + inline QV8Engine* engine() const; + + inline void invalidate(); +private: + Q_DISABLE_COPY(QJSValueIteratorPrivate) + + QIntrusiveListNode m_node; + QScriptSharedDataPointer<QJSValuePrivate> m_object; + v8::Persistent<v8::Array> m_names; + uint32_t m_index; + uint32_t m_count; + + friend class QV8Engine; +}; + + +QT_END_NAMESPACE + +#endif // QJSVALUEITERATOR_P_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp new file mode 100644 index 0000000000..5284832ae1 --- /dev/null +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -0,0 +1,1320 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlbuiltinfunctions_p.h" + +#include <QtQml/qqmlcomponent.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlcomponent_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmllocale_p.h> +#include <private/qv8engine_p.h> +#include <private/qjsconverter_impl_p.h> + +#include <private/qv8profilerservice_p.h> +#include <private/qqmlprofilerservice_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtCore/qpoint.h> +#include <QtCore/qurl.h> +#include <QtCore/qfile.h> +#include <QtCore/qcoreapplication.h> + +#include <QtGui/qcolor.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qdesktopservices.h> +#include <QtGui/qfontdatabase.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlBuiltinFunctions { + +enum ConsoleLogTypes { + Log, + Warn, + Error +}; + +static void jsContext(v8::Handle<v8::Value> *file, int *line, v8::Handle<v8::Value> *function) { + v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(1); + if (stackTrace->GetFrameCount()) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(0); + *file = frame->GetScriptName(); + *line = frame->GetLineNumber(); + *function = frame->GetFunctionName(); + } +} + +static QString jsStack() { + QStringList stackFrames; + + //The v8 default is currently 10 stack frames. + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); + int stackCount = stackTrace->GetFrameCount(); + + for (int i = 0; i < stackCount; i++) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(i); + v8::Handle<v8::String> function(frame->GetFunctionName()); + v8::Handle<v8::String> script(frame->GetScriptName()); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + + QString stackFrame = + QString::fromLatin1("%1 (%2:%3:%4)").arg(QJSConverter::toString(function), + QJSConverter::toString(script), + QString::number(lineNumber), + QString::number(columnNumber)); + stackFrames.append(stackFrame); + } + return stackFrames.join(QLatin1String("\n")); +} + +v8::Handle<v8::Value> console(ConsoleLogTypes logType, const v8::Arguments &args, + bool printStack = false) +{ + v8::HandleScope handleScope; + + QString result; + for (int i = 0; i < args.Length(); ++i) { + if (i != 0) + result.append(QLatin1Char(' ')); + + v8::Local<v8::Value> value = args[i]; + //Check for Object Type + if (value->IsObject() && !value->IsFunction() + && !value->IsArray() && !value->IsDate() + && !value->IsRegExp()) { + result.append(QLatin1String("Object")); + } else { + v8::Local<v8::String> jsstr = value->ToString(); + QString tmp = V8ENGINE()->toString(jsstr); + if (value->IsArray()) + result.append(QString::fromLatin1("[%1]").arg(tmp)); + else + result.append(tmp); + } + } + + if (printStack) { + result.append(QLatin1String("\n")); + result.append(jsStack()); + } + + v8::Handle<v8::Value> fileHandle; + v8::Handle<v8::Value> functionHandle; + int line; + + jsContext(&fileHandle, &line, &functionHandle); + + switch (logType) { + case Log: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).debug("%s", qPrintable(result)); + break; + case Warn: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).warning("%s", qPrintable(result)); + break; + case Error: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).critical("%s", qPrintable(result)); + break; + default: + break; + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> gc(const v8::Arguments &args) +{ + Q_UNUSED(args); + QV8Engine::gc(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleError(const v8::Arguments &args) +{ + return console(Error, args); +} + +v8::Handle<v8::Value> consoleLog(const v8::Arguments &args) +{ + //console.log + //console.debug + //console.info + //print + return console(Log, args); +} + +v8::Handle<v8::Value> consoleProfile(const v8::Arguments &args) +{ + //DeclarativeDebugTrace cannot handle nested profiling + //although v8 can handle several profiling at once, + //we do not allow that. Hence, we pass an empty(default) title + Q_UNUSED(args); + QString title; + + + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + if (QQmlProfilerService::startProfiling()) { + QV8ProfilerService::instance()->startProfiling(title); + + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).debug("Profiling started."); + } else { + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).warning( + "Profiling is already in progress. First, end current profiling session."); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleProfileEnd(const v8::Arguments &args) +{ + //DeclarativeDebugTrace cannot handle nested profiling + //although v8 can handle several profiling at once, + //we do not allow that. Hence, we pass an empty(default) title + Q_UNUSED(args); + QString title; + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + if (QQmlProfilerService::stopProfiling()) { + QV8ProfilerService *profiler = QV8ProfilerService::instance(); + profiler->stopProfiling(title); + QQmlProfilerService::sendProfilingData(); + profiler->sendProfilingData(); + + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).debug("Profiling ended."); + } else { + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).warning("Profiling was not started."); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTime(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("console.time(): Invalid arguments"); + QString name = V8ENGINE()->toString(args[0]); + V8ENGINE()->startTimer(name); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTimeEnd(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("console.time(): Invalid arguments"); + QString name = V8ENGINE()->toString(args[0]); + bool wasRunning; + qint64 elapsed = V8ENGINE()->stopTimer(name, &wasRunning); + if (wasRunning) { + qDebug("%s: %llims", qPrintable(name), elapsed); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleCount(const v8::Arguments &args) +{ + // first argument: name to print. Ignore any additional arguments + QString name; + if (args.Length() > 0) + name = V8ENGINE()->toString(args[0]); + + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(1, v8::StackTrace::kOverview); + + if (stackTrace->GetFrameCount()) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(0); + + QString scriptName = V8ENGINE()->toString(frame->GetScriptName()); + QString functionName = V8ENGINE()->toString(frame->GetFunctionName()); + int line = frame->GetLineNumber(); + int column = frame->GetColumn(); + + int value = V8ENGINE()->consoleCountHelper(scriptName, line, column); + QString message = name + QLatin1String(": ") + QString::number(value); + + QMessageLogger(qPrintable(scriptName), line, + qPrintable(functionName)).debug("%s", qPrintable(message)); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTrace(const v8::Arguments &args) +{ + if (args.Length() != 0) + V8THROW_ERROR("console.trace(): Invalid arguments"); + + QString stack = jsStack(); + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + QMessageLogger(*v8::String::AsciiValue(file), line, *v8::String::AsciiValue(function)).debug( + "%s", qPrintable(stack)); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleWarn(const v8::Arguments &args) +{ + return console(Warn, args); +} + +v8::Handle<v8::Value> consoleAssert(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("console.assert(): Missing argument"); + + if (!args[0]->ToBoolean()->Value()) { + QString message; + for (int i = 1; i < args.Length(); ++i) { + if (i != 1) + message.append(QLatin1Char(' ')); + + v8::Local<v8::Value> value = args[i]; + message.append(V8ENGINE()->toString(value->ToString())); + } + + QString stack = jsStack(); + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + QMessageLogger(*v8::String::AsciiValue(file), line, *v8::String::AsciiValue(function)).critical( + "%s\n%s", qPrintable(message), qPrintable(stack)); + + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleException(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("console.exception(): Missing argument"); + + console(Error, args, true); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> stringArg(const v8::Arguments &args) +{ + QString value = V8ENGINE()->toString(args.This()->ToString()); + if (args.Length() != 1) + V8THROW_ERROR("String.arg(): Invalid arguments"); + + v8::Handle<v8::Value> arg = args[0]; + if (arg->IsUint32()) + return V8ENGINE()->toString(value.arg(arg->Uint32Value())); + else if (arg->IsInt32()) + return V8ENGINE()->toString(value.arg(arg->Int32Value())); + else if (arg->IsNumber()) + return V8ENGINE()->toString(value.arg(arg->NumberValue())); + else if (arg->IsBoolean()) + return V8ENGINE()->toString(value.arg(arg->BooleanValue())); + + return V8ENGINE()->toString(value.arg(V8ENGINE()->toString(arg))); +} + +/*! +\qmlmethod bool Qt::isQtObject(object) +Returns true if \c object is a valid reference to a Qt or QML object, otherwise false. +*/ +v8::Handle<v8::Value> isQtObject(const v8::Arguments &args) +{ + if (args.Length() == 0) + return v8::Boolean::New(false); + + return v8::Boolean::New(0 != V8ENGINE()->toQObject(args[0])); +} + +/*! +\qmlmethod color Qt::rgba(real red, real green, real blue, real alpha) + +Returns a color with the specified \c red, \c green, \c blue and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +v8::Handle<v8::Value> rgba(const v8::Arguments &args) +{ + int argCount = args.Length(); + if (argCount < 3 || argCount > 4) + V8THROW_ERROR("Qt.rgba(): Invalid arguments"); + + double r = args[0]->NumberValue(); + double g = args[1]->NumberValue(); + double b = args[2]->NumberValue(); + double a = (argCount == 4) ? args[3]->NumberValue() : 1; + + if (r < 0.0) r=0.0; + if (r > 1.0) r=1.0; + if (g < 0.0) g=0.0; + if (g > 1.0) g=1.0; + if (b < 0.0) b=0.0; + if (b > 1.0) b=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromRgbF(r, g, b, a))); +} + +/*! +\qmlmethod color Qt::hsla(real hue, real saturation, real lightness, real alpha) + +Returns a color with the specified \c hue, \c saturation, \c lightness and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +v8::Handle<v8::Value> hsla(const v8::Arguments &args) +{ + int argCount = args.Length(); + if (argCount < 3 || argCount > 4) + V8THROW_ERROR("Qt.hsla(): Invalid arguments"); + + double h = args[0]->NumberValue(); + double s = args[1]->NumberValue(); + double l = args[2]->NumberValue(); + double a = (argCount == 4) ? args[3]->NumberValue() : 1; + + if (h < 0.0) h=0.0; + if (h > 1.0) h=1.0; + if (s < 0.0) s=0.0; + if (s > 1.0) s=1.0; + if (l < 0.0) l=0.0; + if (l > 1.0) l=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromHslF(h, s, l, a))); +} + +/*! +\qmlmethod rect Qt::rect(int x, int y, int width, int height) + +Returns a \c rect with the top-left corner at \c x, \c y and the specified \c width and \c height. + +The returned object has \c x, \c y, \c width and \c height attributes with the given values. +*/ +v8::Handle<v8::Value> rect(const v8::Arguments &args) +{ + if (args.Length() != 4) + V8THROW_ERROR("Qt.rect(): Invalid arguments"); + + double x = args[0]->NumberValue(); + double y = args[1]->NumberValue(); + double w = args[2]->NumberValue(); + double h = args[3]->NumberValue(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QRectF(x, y, w, h))); +} + +/*! +\qmlmethod point Qt::point(int x, int y) +Returns a Point with the specified \c x and \c y coordinates. +*/ +v8::Handle<v8::Value> point(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.point(): Invalid arguments"); + + double x = args[0]->ToNumber()->Value(); + double y = args[1]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QPointF(x, y))); +} + +/*! +\qmlmethod Qt::size(int width, int height) +Returns a Size with the specified \c width and \c height. +*/ +v8::Handle<v8::Value> size(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.size(): Invalid arguments"); + + double w = args[0]->ToNumber()->Value(); + double h = args[1]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QSizeF(w, h))); +} + +/*! +\qmlmethod Qt::vector3d(real x, real y, real z) +Returns a Vector3D with the specified \c x, \c y and \c z. +*/ +v8::Handle<v8::Value> vector3d(const v8::Arguments &args) +{ + if (args.Length() != 3) + V8THROW_ERROR("Qt.vector(): Invalid arguments"); + + double x = args[0]->ToNumber()->Value(); + double y = args[1]->ToNumber()->Value(); + double z = args[2]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QVector3D(x, y, z))); +} + +/*! +\qmlmethod Qt::vector4d(real x, real y, real z, real w) +Returns a Vector4D with the specified \c x, \c y, \c z and \c w. +*/ +v8::Handle<v8::Value> vector4d(const v8::Arguments &args) +{ + if (args.Length() != 4) + V8THROW_ERROR("Qt.vector4d(): Invalid arguments"); + + double x = args[0]->NumberValue(); + double y = args[1]->NumberValue(); + double z = args[2]->NumberValue(); + double w = args[3]->NumberValue(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QVector4D(x, y, z, w))); +} + +/*! +\qmlmethod color Qt::lighter(color baseColor, real factor) +Returns a color lighter than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this functions returns a lighter color. +Setting factor to 1.5 returns a color that is 50% brighter. If the factor is less than 1.0, +the return color is darker, but we recommend using the Qt.darker() function for this purpose. +If the factor is 0 or negative, the return value is unspecified. + +The function converts the current RGB color to HSV, multiplies the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% lighter than \c baseColor (factor 1.5). +*/ +v8::Handle<v8::Value> lighter(const v8::Arguments &args) +{ + if (args.Length() != 1 && args.Length() != 2) + V8THROW_ERROR("Qt.lighter(): Invalid arguments"); + + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + qreal factor = 1.5; + if (args.Length() == 2) + factor = args[1]->ToNumber()->Value(); + + color = color.lighter(int(qRound(factor*100.))); + return V8ENGINE()->fromVariant(QVariant::fromValue(color)); +} + +/*! +\qmlmethod color Qt::darker(color baseColor, real factor) +Returns a color darker than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this function returns a darker color. +Setting factor to 3.0 returns a color that has one-third the brightness. +If the factor is less than 1.0, the return color is lighter, but we recommend using +the Qt.lighter() function for this purpose. If the factor is 0 or negative, the return +value is unspecified. + +The function converts the current RGB color to HSV, divides the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% darker than \c baseColor (factor 2.0). +*/ +v8::Handle<v8::Value> darker(const v8::Arguments &args) +{ + if (args.Length() != 1 && args.Length() != 2) + V8THROW_ERROR("Qt.darker(): Invalid arguments"); + + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + qreal factor = 2.0; + if (args.Length() == 2) + factor = args[1]->ToNumber()->Value(); + + color = color.darker(int(qRound(factor*100.))); + return V8ENGINE()->fromVariant(QVariant::fromValue(color)); +} + +/*! + \qmlmethod color Qt::tint(color baseColor, color tintColor) + This function allows tinting one color with another. + + The tint color should usually be mostly transparent, or you will not be + able to see the underlying color. The below example provides a slight red + tint by having the tint color be pure red which is only 1/16th opaque. + + \qml + Item { + Rectangle { + x: 0; width: 80; height: 80 + color: "lightsteelblue" + } + Rectangle { + x: 100; width: 80; height: 80 + color: Qt.tint("lightsteelblue", "#10FF0000") + } + } + \endqml + \image declarative-rect_tint.png + + Tint is most useful when a subtle change is intended to be conveyed due to some event; you can then use tinting to more effectively tune the visible color. +*/ +v8::Handle<v8::Value> tint(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.tint(): Invalid arguments"); + + // base color + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + // tint color + QColor tintColor; + v = V8ENGINE()->toVariant(args[1], -1); + if (v.userType() == QVariant::Color) { + tintColor = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + tintColor = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + // tint the base color and return the final color + QColor finalColor; + int a = tintColor.alpha(); + if (a == 0xFF) + finalColor = tintColor; + else if (a == 0x00) + finalColor = color; + else { + qreal a = tintColor.alphaF(); + qreal inv_a = 1.0 - a; + + finalColor.setRgbF(tintColor.redF() * a + color.redF() * inv_a, + tintColor.greenF() * a + color.greenF() * inv_a, + tintColor.blueF() * a + color.blueF() * inv_a, + a + inv_a * color.alphaF()); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(finalColor)); +} + +/*! +\qmlmethod string Qt::formatDate(datetime date, variant format) + +Returns a string representation of \c date, optionally formatted according +to \c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, or QDateTime value. The \a format parameter may be any of +the possible format values as described for +\l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a date is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. + +\sa Locale +*/ +v8::Handle<v8::Value> formatDate(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatDate(): Invalid arguments"); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QDate date = V8ENGINE()->toVariant(args[0], -1).toDateTime().date(); + QString formattedDate; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedDate = date.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedDate = date.toString(format); + } else { + V8THROW_ERROR("Qt.formatDate(): Invalid date format"); + } + } else { + formattedDate = date.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedDate)); +} + +/*! +\qmlmethod string Qt::formatTime(datetime time, variant format) + +Returns a string representation of \c time, optionally formatted according to +\c format. + +The \a time parameter may be a JavaScript \c Date object, a QTime, or QDateTime +value. The \a format parameter may be any of the possible format values as +described for \l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a time is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. + +\sa Locale +*/ +v8::Handle<v8::Value> formatTime(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatTime(): Invalid arguments"); + + QVariant argVariant = V8ENGINE()->toVariant(args[0], -1); + QTime time; + if (args[0]->IsDate() || (argVariant.type() == QVariant::String)) + time = argVariant.toDateTime().time(); + else // if (argVariant.type() == QVariant::Time), or invalid. + time = argVariant.toTime(); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QString formattedTime; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedTime = time.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedTime = time.toString(format); + } else { + V8THROW_ERROR("Qt.formatTime(): Invalid time format"); + } + } else { + formattedTime = time.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedTime)); +} + +/*! +\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format) + +Returns a string representation of \c datetime, optionally formatted according to +\c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, QTime, or QDateTime value. + +If \a format is not provided, \a dateTime is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. Otherwise, +\a format should be either: + +\list +\o One of the Qt::DateFormat enumeration values, such as + \c Qt.DefaultLocaleShortDate or \c Qt.ISODate +\o A string that specifies the format of the returned string, as detailed below. +\endlist + +If \a format specifies a format string, it should use the following expressions +to specify the date: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1-12) + \row \i MM \i the month as number with a leading zero (01-12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00-99) + \row \i yyyy \i the year as four digit number + \endtable + +In addition the following expressions can be used to specify the time: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP + \i use AM/PM display. \e AP will be replaced by either "AM" or "PM". + \row \i ap + \i use am/pm display. \e ap will be replaced by either "am" or "pm". + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in single quotes will be treated as text and not be used as an + expression. Two consecutive single quotes ("''") are replaced by a single quote + in the output. + +For example, if the following date/time value was specified: + + \code + // 21 May 2001 14:13:09 + var dateTime = new Date(2001, 5, 21, 14, 13, 09) + \endcode + +This \a dateTime value could be passed to \c Qt.formatDateTime(), +\l {QML:Qt::formatDate()}{Qt.formatDate()} or \l {QML:Qt::formatTime()}{Qt.formatTime()} +with the \a format values below to produce the following results: + + \table + \header \i Format \i Result + \row \i "dd.MM.yyyy" \i 21.05.2001 + \row \i "ddd MMMM d yy" \i Tue May 21 01 + \row \i "hh:mm:ss.zzz" \i 14:13:09.042 + \row \i "h:m:s ap" \i 2:13:9 pm + \endtable + + \sa Locale +*/ +v8::Handle<v8::Value> formatDateTime(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatDateTime(): Invalid arguments"); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QDateTime dt = V8ENGINE()->toVariant(args[0], -1).toDateTime(); + QString formattedDt; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedDt = dt.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedDt = dt.toString(format); + } else { + V8THROW_ERROR("Qt.formatDateTime(): Invalid datetime format"); + } + } else { + formattedDt = dt.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedDt)); +} + +/*! +\qmlmethod bool Qt::openUrlExternally(url target) +Attempts to open the specified \c target url in an external application, based on the user's desktop preferences. Returns true if it succeeds, and false otherwise. +*/ +v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args) +{ + if (args.Length() != 1) + return V8ENGINE()->fromVariant(false); + + bool ret = false; +#ifndef QT_NO_DESKTOPSERVICES + ret = QDesktopServices::openUrl(V8ENGINE()->toVariant(resolvedUrl(args), -1).toUrl()); +#endif + return V8ENGINE()->fromVariant(ret); +} + +/*! + \qmlmethod url Qt::resolvedUrl(url url) + Returns \a url resolved relative to the URL of the caller. +*/ +v8::Handle<v8::Value> resolvedUrl(const v8::Arguments &args) +{ + QUrl url = V8ENGINE()->toVariant(args[0], -1).toUrl(); + QQmlEngine *e = V8ENGINE()->engine(); + QQmlEnginePrivate *p = 0; + if (e) p = QQmlEnginePrivate::get(e); + if (p) { + QQmlContextData *ctxt = V8ENGINE()->callingContext(); + if (ctxt) + return V8ENGINE()->toString(ctxt->resolvedUrl(url).toString()); + else + return V8ENGINE()->toString(url.toString()); + } + + return V8ENGINE()->toString(e->baseUrl().resolved(url).toString()); +} + +/*! +\qmlmethod list<string> Qt::fontFamilies() +Returns a list of the font families available to the application. +*/ +v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args) +{ + if (args.Length() != 0) + V8THROW_ERROR("Qt.fontFamilies(): Invalid arguments"); + + QFontDatabase database; + return V8ENGINE()->fromVariant(database.families()); +} + +/*! +\qmlmethod string Qt::md5(data) +Returns a hex string of the md5 hash of \c data. +*/ +v8::Handle<v8::Value> md5(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.md5(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5); + return V8ENGINE()->toString(QLatin1String(result.toHex())); +} + +/*! +\qmlmethod string Qt::btoa(data) +Binary to ASCII - this function returns a base64 encoding of \c data. +*/ +v8::Handle<v8::Value> btoa(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.btoa(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + + return V8ENGINE()->toString(QLatin1String(data.toBase64())); +} + +/*! +\qmlmethod string Qt::atob(data) +ASCII to binary - this function returns a base64 decoding of \c data. +*/ +v8::Handle<v8::Value> atob(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.atob(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + + return V8ENGINE()->toString(QLatin1String(QByteArray::fromBase64(data))); +} + +/*! +\qmlmethod Qt::quit() +This function causes the QQmlEngine::quit() signal to be emitted. +Within the \l {QML Viewer}, this causes the launcher application to exit; +to quit a C++ application when this method is called, connect the +QQmlEngine::quit() signal to the QCoreApplication::quit() slot. +*/ +v8::Handle<v8::Value> quit(const v8::Arguments &args) +{ + QQmlEnginePrivate::get(V8ENGINE()->engine())->sendQuit(); + return v8::Undefined(); +} + +/*! +\qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath) + +Returns a new object created from the given \a string of QML which will have the specified \a parent, +or \c null if there was an error in creating the object. + +If \a filepath is specified, it will be used for error reporting for the created object. + +Example (where \c parentItem is the id of an existing QML item): + +\snippet doc/src/snippets/qml/createQmlObject.qml 0 + +In the case of an error, a QtScript Error object is thrown. This object has an additional property, +\c qmlErrors, which is an array of the errors encountered. +Each object in this array has the members \c lineNumber, \c columnNumber, \c fileName and \c message. +For example, if the above snippet had misspelled color as 'colro' then the array would contain an object like the following: +{ "lineNumber" : 1, "columnNumber" : 32, "fileName" : "dynamicSnippet1", "message" : "Cannot assign to non-existent property \"colro\""}. + +Note that this function returns immediately, and therefore may not work if +the \a qml string loads new components (that is, external QML files that have not yet been loaded). +If this is the case, consider using \l{QML:Qt::createComponent()}{Qt.createComponent()} instead. + +See \l {Dynamic Object Management in QML} for more information on using this function. +*/ +v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args) +{ + if (args.Length() < 2 || args.Length() > 3) + V8THROW_ERROR("Qt.createQmlObject(): Invalid arguments"); + + struct Error { + static v8::Local<v8::Value> create(QV8Engine *engine, const QList<QQmlError> &errors) { + QString errorstr = QLatin1String("Qt.createQmlObject(): failed to create object: "); + + v8::Local<v8::Array> qmlerrors = v8::Array::New(errors.count()); + for (int ii = 0; ii < errors.count(); ++ii) { + const QQmlError &error = errors.at(ii); + errorstr += QLatin1String("\n ") + error.toString(); + v8::Local<v8::Object> qmlerror = v8::Object::New(); + qmlerror->Set(v8::String::New("lineNumber"), v8::Integer::New(error.line())); + qmlerror->Set(v8::String::New("columnNumber"), v8::Integer::New(error.column())); + qmlerror->Set(v8::String::New("fileName"), engine->toString(error.url().toString())); + qmlerror->Set(v8::String::New("message"), engine->toString(error.description())); + qmlerrors->Set(ii, qmlerror); + } + + v8::Local<v8::Value> error = v8::Exception::Error(engine->toString(errorstr)); + v8::Local<v8::Object> errorObject = error->ToObject(); + errorObject->Set(v8::String::New("qmlErrors"), qmlerrors); + return error; + } + }; + + QV8Engine *v8engine = V8ENGINE(); + QQmlEngine *engine = v8engine->engine(); + + QQmlContextData *context = v8engine->callingContext(); + QQmlContext *effectiveContext = 0; + if (context->isPragmaLibraryContext) + effectiveContext = engine->rootContext(); + else + effectiveContext = context->asQQmlContext(); + Q_ASSERT(context && effectiveContext); + + QString qml = v8engine->toString(args[0]->ToString()); + if (qml.isEmpty()) + return v8::Null(); + + QUrl url; + if (args.Length() > 2) + url = QUrl(v8engine->toString(args[2]->ToString())); + else + url = QUrl(QLatin1String("inline")); + + if (url.isValid() && url.isRelative()) + url = context->resolvedUrl(url); + + QObject *parentArg = v8engine->toQObject(args[1]); + if (!parentArg) + V8THROW_ERROR("Qt.createQmlObject(): Missing parent object"); + + QQmlComponent component(engine); + component.setData(qml.toUtf8(), url); + + if (component.isError()) { + v8::ThrowException(Error::create(v8engine, component.errors())); + return v8::Undefined(); + } + + if (!component.isReady()) + V8THROW_ERROR("Qt.createQmlObject(): Component is not ready"); + + QObject *obj = component.beginCreate(effectiveContext); + if (obj) + QQmlData::get(obj, true)->setImplicitDestructible(); + component.completeCreate(); + + if (component.isError()) { + v8::ThrowException(Error::create(v8engine, component.errors())); + return v8::Undefined(); + } + + Q_ASSERT(obj); + + obj->setParent(parentArg); + + QList<QQmlPrivate::AutoParentFunction> functions = QQmlMetaType::parentFunctions(); + for (int ii = 0; ii < functions.count(); ++ii) { + if (QQmlPrivate::Parented == functions.at(ii)(obj, parentArg)) + break; + } + + return v8engine->newQObject(obj); +} + +/*! +\qmlmethod object Qt::createComponent(url) + +Returns a \l Component object created using the QML file at the specified \a url, +or \c null if an empty string was given. + +The returned component's \l Component::status property indicates whether the +component was successfully created. If the status is \c Component.Error, +see \l Component::errorString() for an error description. + +Call \l {Component::createObject()}{Component.createObject()} on the returned +component to create an object instance of the component. + +For example: + +\snippet doc/src/snippets/qml/createComponent-simple.qml 0 + +See \l {Dynamic Object Management in QML} for more information on using this function. + +To create a QML object from an arbitrary string of QML (instead of a file), +use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}. +*/ +v8::Handle<v8::Value> createComponent(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.createComponent(): Invalid arguments"); + + QV8Engine *v8engine = V8ENGINE(); + QQmlEngine *engine = v8engine->engine(); + + QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *effectiveContext = context; + if (context->isPragmaLibraryContext) + effectiveContext = 0; + Q_ASSERT(context); + + QString arg = v8engine->toString(args[0]->ToString()); + if (arg.isEmpty()) + return v8::Null(); + + QUrl url = context->resolvedUrl(QUrl(arg)); + QQmlComponent *c = new QQmlComponent(engine, url, engine); + QQmlComponentPrivate::get(c)->creationContext = effectiveContext; + QQmlData::get(c, true)->setImplicitDestructible(); + return v8engine->newQObject(c); +} + +v8::Handle<v8::Value> qsTranslate(const v8::Arguments &args) +{ + if (args.Length() < 2) + V8THROW_ERROR("qsTranslate() requires at least two arguments"); + if (!args[0]->IsString()) + V8THROW_ERROR("qsTranslate(): first argument (context) must be a string"); + if (!args[1]->IsString()) + V8THROW_ERROR("qsTranslate(): second argument (text) must be a string"); + if ((args.Length() > 2) && !args[2]->IsString()) + V8THROW_ERROR("qsTranslate(): third argument (comment) must be a string"); + if ((args.Length() > 3) && !args[3]->IsString()) + V8THROW_ERROR("qsTranslate(): fourth argument (encoding) must be a string"); + + QV8Engine *v8engine = V8ENGINE(); + QString context = v8engine->toString(args[0]); + QString text = v8engine->toString(args[1]); + QString comment; + if (args.Length() > 2) comment = v8engine->toString(args[2]); + + QCoreApplication::Encoding encoding = QCoreApplication::UnicodeUTF8; + if (args.Length() > 3) { + QString encStr = v8engine->toString(args[3]); + if (encStr == QLatin1String("CodecForTr")) { + encoding = QCoreApplication::CodecForTr; + } else if (encStr == QLatin1String("UnicodeUTF8")) { + encoding = QCoreApplication::UnicodeUTF8; + } else { + QString msg = QString::fromLatin1("qsTranslate(): invalid encoding '%0'").arg(encStr); + V8THROW_ERROR((uint16_t *)msg.constData()); + } + } + + int n = -1; + if (args.Length() > 4) + n = args[4]->Int32Value(); + + QString result = QCoreApplication::translate(context.toUtf8().constData(), + text.toUtf8().constData(), + comment.toUtf8().constData(), + encoding, n); + + return v8engine->toString(result); +} + +v8::Handle<v8::Value> qsTranslateNoOp(const v8::Arguments &args) +{ + if (args.Length() < 2) + return v8::Undefined(); + return args[1]; +} + +v8::Handle<v8::Value> qsTr(const v8::Arguments &args) +{ + if (args.Length() < 1) + V8THROW_ERROR("qsTr() requires at least one argument"); + if (!args[0]->IsString()) + V8THROW_ERROR("qsTr(): first argument (text) must be a string"); + if ((args.Length() > 1) && !args[1]->IsString()) + V8THROW_ERROR("qsTr(): second argument (comment) must be a string"); + if ((args.Length() > 2) && !args[2]->IsNumber()) + V8THROW_ERROR("qsTr(): third argument (n) must be a number"); + + QV8Engine *v8engine = V8ENGINE(); + QQmlContextData *ctxt = v8engine->callingContext(); + + QString path = ctxt->url.toString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) : QString(); + + QString text = v8engine->toString(args[0]); + QString comment; + if (args.Length() > 1) + comment = v8engine->toString(args[1]); + int n = -1; + if (args.Length() > 2) + n = args[2]->Int32Value(); + + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), + comment.toUtf8().constData(), QCoreApplication::UnicodeUTF8, n); + + return v8engine->toString(result); +} + +v8::Handle<v8::Value> qsTrNoOp(const v8::Arguments &args) +{ + if (args.Length() < 1) + return v8::Undefined(); + return args[0]; +} + +v8::Handle<v8::Value> qsTrId(const v8::Arguments &args) +{ + if (args.Length() < 1) + V8THROW_ERROR("qsTrId() requires at least one argument"); + if (!args[0]->IsString()) + V8THROW_TYPE("qsTrId(): first argument (id) must be a string"); + if (args.Length() > 1 && !args[1]->IsNumber()) + V8THROW_TYPE("qsTrId(): second argument (n) must be a number"); + + int n = -1; + if (args.Length() > 1) + n = args[1]->Int32Value(); + + QV8Engine *v8engine = V8ENGINE(); + return v8engine->toString(qtTrId(v8engine->toString(args[0]).toUtf8().constData(), n)); +} + +v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args) +{ + if (args.Length() < 1) + return v8::Undefined(); + return args[0]; +} + + +/*! + \qmlmethod Qt::locale(name) + + Returns a JS object representing the locale with the specified + name, which has the format "language[_territory][.codeset][@modifier]" + or "C", where: + + \list + \o language is a lowercase, two-letter, ISO 639 language code, + \o territory is an uppercase, two-letter, ISO 3166 country code, + \o and codeset and modifier are ignored. + \endlist + + If the string violates the locale format, or language is not a + valid ISO 369 code, the "C" locale is used instead. If country + is not present, or is not a valid ISO 3166 code, the most + appropriate country is chosen for the specified language. + + \sa QtQuick2::Locale +*/ +v8::Handle<v8::Value> locale(const v8::Arguments &args) +{ + QString code; + if (args.Length() > 1) + V8THROW_ERROR("locale() requires 0 or 1 argument"); + if (args.Length() == 1 && !args[0]->IsString()) + V8THROW_TYPE("locale(): argument (locale code) must be a string"); + + QV8Engine *v8engine = V8ENGINE(); + if (args.Length() == 1) + code = v8engine->toString(args[0]); + + return QQmlLocale::locale(v8engine, code); +} + +} // namespace QQmlBuiltinFunctions + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h new file mode 100644 index 0000000000..ddb1c64243 --- /dev/null +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBUILTINFUNCTIONS_P_H +#define QQMLBUILTINFUNCTIONS_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 <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlBuiltinFunctions +{ +v8::Handle<v8::Value> gc(const v8::Arguments &args); +v8::Handle<v8::Value> consoleError(const v8::Arguments &args); +v8::Handle<v8::Value> consoleLog(const v8::Arguments &args); +v8::Handle<v8::Value> consoleProfile(const v8::Arguments &args); +v8::Handle<v8::Value> consoleProfileEnd(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTime(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTimeEnd(const v8::Arguments &args); +v8::Handle<v8::Value> consoleCount(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTrace(const v8::Arguments &args); +v8::Handle<v8::Value> consoleWarn(const v8::Arguments &args); +v8::Handle<v8::Value> consoleAssert(const v8::Arguments &args); +v8::Handle<v8::Value> consoleException(const v8::Arguments &args); +v8::Handle<v8::Value> isQtObject(const v8::Arguments &args); +v8::Handle<v8::Value> rgba(const v8::Arguments &args); +v8::Handle<v8::Value> hsla(const v8::Arguments &args); +v8::Handle<v8::Value> rect(const v8::Arguments &args); +v8::Handle<v8::Value> point(const v8::Arguments &args); +v8::Handle<v8::Value> size(const v8::Arguments &args); +v8::Handle<v8::Value> vector3d(const v8::Arguments &args); +v8::Handle<v8::Value> vector4d(const v8::Arguments &args); +v8::Handle<v8::Value> lighter(const v8::Arguments &args); +v8::Handle<v8::Value> darker(const v8::Arguments &args); +v8::Handle<v8::Value> tint(const v8::Arguments &args); +v8::Handle<v8::Value> formatDate(const v8::Arguments &args); +v8::Handle<v8::Value> formatTime(const v8::Arguments &args); +v8::Handle<v8::Value> formatDateTime(const v8::Arguments &args); +v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args); +v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args); +v8::Handle<v8::Value> md5(const v8::Arguments &args); +v8::Handle<v8::Value> btoa(const v8::Arguments &args); +v8::Handle<v8::Value> atob(const v8::Arguments &args); +v8::Handle<v8::Value> quit(const v8::Arguments &args); +v8::Handle<v8::Value> resolvedUrl(const v8::Arguments &args); +v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args); +v8::Handle<v8::Value> createComponent(const v8::Arguments &args); +v8::Handle<v8::Value> qsTranslate(const v8::Arguments &args); +v8::Handle<v8::Value> qsTranslateNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> qsTr(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrId(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> stringArg(const v8::Arguments &args); +v8::Handle<v8::Value> locale(const v8::Arguments &args); +} + +QT_END_NAMESPACE + +#endif // QQMLBUILTINFUNCTIONS_P_H diff --git a/src/qml/qml/v8/qscript_impl_p.h b/src/qml/qml/v8/qscript_impl_p.h new file mode 100644 index 0000000000..fdbf2f0097 --- /dev/null +++ b/src/qml/qml/v8/qscript_impl_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QSCRIPT_IMPL_P_H +#define QSCRIPT_IMPL_P_H + +#include "qv8engine_impl_p.h" +#include "qjsvalue_impl_p.h" +#include "qjsvalueiterator_impl_p.h" +#include "qjsconverter_impl_p.h" + +#endif //QSCRIPT_IMPL_P_H diff --git a/src/qml/qml/v8/qscriptisolate_p.h b/src/qml/qml/v8/qscriptisolate_p.h new file mode 100644 index 0000000000..4afa74756f --- /dev/null +++ b/src/qml/qml/v8/qscriptisolate_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APIPREAMBLE_P_H +#define APIPREAMBLE_P_H + +#include <private/qv8_p.h> +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +/** + \internal + Class used to switch to the right isolate. It does the same thing as v8::Isolate::Scope but + it checks for a null engine. + \attention We decided to put context switching "up" which means that it should be as high + as possible on call stack. And it should be switched at most once per public API function call. +*/ +class QScriptIsolate { +public: + // OperationMode was introduced to reduce number of checking for a null engine pointer. If we + // know that given pointer is not null than we should pass NotNullEngine as constructor argument + // that would nicely remove checking on compilation time. + enum OperationMode {Default, NotNullEngine}; + inline QScriptIsolate(const QV8Engine *engine, const OperationMode mode = Default) + : m_engine(engine) + , m_mode(mode) + { + if (m_mode == NotNullEngine || m_engine) { + Q_ASSERT(m_engine); + m_engine->context()->Enter(); + } + } + + inline ~QScriptIsolate() + { + if (m_mode == NotNullEngine || m_engine) { + m_engine->context()->Exit(); + } + } + +private: + Q_DISABLE_COPY(QScriptIsolate); + const QV8Engine *m_engine; + const OperationMode m_mode; +}; + + +QT_END_NAMESPACE + +#endif // APIPREAMBLE_P_H diff --git a/src/qml/qml/v8/qscriptoriginalglobalobject_p.h b/src/qml/qml/v8/qscriptoriginalglobalobject_p.h new file mode 100644 index 0000000000..12321cc71a --- /dev/null +++ b/src/qml/qml/v8/qscriptoriginalglobalobject_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTORIGINALGLOBALOBJECT_P_H +#define QSCRIPTORIGINALGLOBALOBJECT_P_H + +#include "QtCore/qglobal.h" +#include "qjsvalue_p.h" + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +/*! + \internal + This class is a workaround for missing V8 API functionality. This class keeps all important + properties of an original (default) global object, so we can use it even if the global object was + changed. + + FIXME this class is a container for workarounds :-) it should be replaced by proper API calls. + + The class have to be created on the QV8Engine creation time (before any change got applied to + global object). + + \attention All methods (apart from constructor) assumes that a context and a scope are prepared correctly. +*/ +class QScriptOriginalGlobalObject +{ +public: + inline QScriptOriginalGlobalObject() {} + inline void init(v8::Handle<v8::Context> context); + inline void destroy(); + + inline QJSValuePrivate::PropertyFlags getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property); + inline v8::Local<v8::Object> getOwnPropertyDescriptor(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) const; + inline bool strictlyEquals(v8::Handle<v8::Object> object); +private: + Q_DISABLE_COPY(QScriptOriginalGlobalObject) + + // Copy of constructors and prototypes used in isType functions. + v8::Persistent<v8::Function> m_ownPropertyDescriptor; + v8::Persistent<v8::Object> m_globalObject; +}; + +void QScriptOriginalGlobalObject::init(v8::Handle<v8::Context> context) +{ + // Please notice that engine is not fully initialized at this point. + + v8::Context::Scope contextScope(context); + + v8::HandleScope scope; + + m_globalObject = v8::Persistent<v8::Object>::New(context->Global()); + + v8::Local<v8::Object> objectConstructor = m_globalObject->Get(v8::String::New("Object"))->ToObject(); + Q_ASSERT(objectConstructor->IsObject()); + { // Initialize m_ownPropertyDescriptor. + v8::Local<v8::Value> ownPropertyDescriptor = objectConstructor->Get(v8::String::New("getOwnPropertyDescriptor")); + Q_ASSERT(!ownPropertyDescriptor.IsEmpty()); + m_ownPropertyDescriptor = v8::Persistent<v8::Function>::New(v8::Local<v8::Function>::Cast(ownPropertyDescriptor)); + } +} + +/*! + \internal + QScriptOriginalGlobalObject lives as long as QV8Engine that keeps it. In ~QSEP + the v8 context is removed, so we need to remove our handlers before. to break this dependency + destroy method should be called before or insight QSEP destructor. +*/ +inline void QScriptOriginalGlobalObject::destroy() +{ + m_ownPropertyDescriptor.Dispose(); + m_globalObject.Dispose(); + // After this line this instance is unusable. +} + +inline QJSValuePrivate::PropertyFlags QScriptOriginalGlobalObject::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + v8::Local<v8::Object> descriptor = getOwnPropertyDescriptor(object, property); + if (descriptor.IsEmpty()) { +// // Property isn't owned by this object. +// if (!(mode & QScriptValue::ResolvePrototype)) +// return 0; + v8::Local<v8::Value> prototype = object->GetPrototype(); + if (prototype->IsNull()) + return 0; + return getPropertyFlags(v8::Local<v8::Object>::Cast(prototype), property); + } + v8::Local<v8::String> writableName = v8::String::New("writable"); + v8::Local<v8::String> configurableName = v8::String::New("configurable"); + v8::Local<v8::String> enumerableName = v8::String::New("enumerable"); +// v8::Local<v8::String> getName = v8::String::New("get"); +// v8::Local<v8::String> setName = v8::String::New("set"); + + unsigned flags = 0; + + if (!descriptor->Get(configurableName)->BooleanValue()) + flags |= QJSValuePrivate::Undeletable; + if (!descriptor->Get(enumerableName)->BooleanValue()) + flags |= QJSValuePrivate::SkipInEnumeration; + + //"writable" is only a property of the descriptor if it is not an accessor + if (descriptor->Has(writableName)) { + if (!descriptor->Get(writableName)->BooleanValue()) + flags |= QJSValuePrivate::ReadOnly; + } else { +// if (descriptor->Get(getName)->IsObject()) +// flags |= QScriptValue::PropertyGetter; +// if (descriptor->Get(setName)->IsObject()) +// flags |= QScriptValue::PropertySetter; + } + + return QJSValuePrivate::PropertyFlag(flags); +} + +inline v8::Local<v8::Object> QScriptOriginalGlobalObject::getOwnPropertyDescriptor(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) const +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + // FIXME do we need try catch here? + v8::Handle<v8::Value> argv[] = {object, property}; + v8::Local<v8::Value> descriptor = m_ownPropertyDescriptor->Call(m_globalObject, /* argc */ 2, argv); + if (descriptor.IsEmpty() || !descriptor->IsObject()) + return v8::Local<v8::Object>(); + return v8::Local<v8::Object>::Cast(descriptor); +} + +inline bool QScriptOriginalGlobalObject::strictlyEquals(v8::Handle<v8::Object> object) +{ + return m_globalObject->GetPrototype()->StrictEquals(object); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qscriptshareddata_p.h b/src/qml/qml/v8/qscriptshareddata_p.h new file mode 100644 index 0000000000..df95b26206 --- /dev/null +++ b/src/qml/qml/v8/qscriptshareddata_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QSCRIPTSHAREDDATA_P_H +#define QSCRIPTSHAREDDATA_P_H + +#include "qglobal.h" +#include "qshareddata.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + This class should have the same interface as the QSharedData, but implementation doesn't + need to be thread safe, so atomic ref count was replaced by normal integer value. +*/ +class QScriptSharedData +{ +public: + class ReferenceCounter { + // FIXME shouldn't it be uint or something longer? + mutable int m_ref; + ReferenceCounter(int ref) : m_ref(ref) {} + ~ReferenceCounter() { Q_ASSERT_X(!m_ref, Q_FUNC_INFO, "Memory problem found"); } + public: + bool ref() { return ++m_ref; } + bool deref() { return --m_ref; } + friend class QScriptSharedData; + }; + + ReferenceCounter ref; + inline QScriptSharedData() : ref(0) { } + +private: + Q_DISABLE_COPY(QScriptSharedData) +}; + + +template <class T> class QScriptPassPointer; + +// FIXME: that could be reimplemented to not check for a null value. +template<class T> +class QScriptSharedDataPointer : public QExplicitlySharedDataPointer<T> +{ +public: + inline QScriptSharedDataPointer() {} + explicit QScriptSharedDataPointer(QScriptPassPointer<T> data) : QExplicitlySharedDataPointer<T>(data.give()) {} + explicit QScriptSharedDataPointer(T *data) : QExplicitlySharedDataPointer<T>(data) {} + + inline QScriptSharedDataPointer<T> &operator=(const QScriptPassPointer<T> &other) + { + this->QExplicitlySharedDataPointer<T>::operator =(other.give()); + return *this; + } + inline QScriptSharedDataPointer<T> &operator=(T *other) + { + this->QExplicitlySharedDataPointer<T>::operator =(other); + return *this; + } +}; + +// FIXME: that could be reimplemented to not check for a null value. +template <class T> +class QScriptPassPointer { +public: + QScriptPassPointer(T *data) : m_ptr(data) {} + inline QScriptPassPointer() { m_ptr = 0; } + inline QScriptPassPointer(const QScriptPassPointer<T> &other) : m_ptr(other.give()) {} + inline ~QScriptPassPointer() { Q_ASSERT_X(!m_ptr, Q_FUNC_INFO, "Ownership of the QScriptPassPointer hasn't been taken"); } + + inline T &operator*() const { return *m_ptr; } + inline T *operator->() { return m_ptr; } + inline T *operator->() const { return m_ptr; } + inline T *data() const { return m_ptr; } + inline const T *constData() const { return m_ptr; } + + inline bool operator==(const QScriptPassPointer<T> &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptPassPointer<T> &other) const { return m_ptr != other.m_ptr; } + inline bool operator==(const QScriptSharedDataPointer<T> &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptSharedDataPointer<T> &other) const { return m_ptr != other.m_ptr; } + inline bool operator==(const T *ptr) const { return m_ptr == ptr; } + inline bool operator!=(const T *ptr) const { return m_ptr != ptr; } + + inline operator bool () const { return m_ptr != 0; } + inline bool operator!() const { return !m_ptr; } + + inline QScriptPassPointer<T> & operator=(const QScriptPassPointer<T> &other) + { + if (other.m_ptr != m_ptr) { + if (m_ptr) + delete m_ptr; + m_ptr = other.give(); + } + return *this; + } + + inline QScriptPassPointer &operator=(T *other) + { + if (other != m_ptr) { + if (m_ptr) + delete m_ptr; + m_ptr = other; + } + return *this; + } + + inline T* give() const + { + T* result = m_ptr; + m_ptr = 0; + return result; + } + +private: + mutable T* m_ptr; +}; + +QT_END_NAMESPACE + +#endif // QSCRIPTSHAREDDATA_P_H diff --git a/src/qml/qml/v8/qscripttools_p.h b/src/qml/qml/v8/qscripttools_p.h new file mode 100644 index 0000000000..fcea205f61 --- /dev/null +++ b/src/qml/qml/v8/qscripttools_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + + +#ifndef QSCRIPTTOOLS_P_H +#define QSCRIPTTOOLS_P_H + +#include <private/qintrusivelist_p.h> + +QT_BEGIN_NAMESPACE + +template<class N, QIntrusiveListNode N::*member> +class QScriptIntrusiveList : public QIntrusiveList<N, member> +{ +public: + inline void insert(N *n); + inline void remove(N *n); +}; + +template<class N, QIntrusiveListNode N::*member> +void QScriptIntrusiveList<N, member>::insert(N *n) +{ + Q_ASSERT_X(!this->contains(n), Q_FUNC_INFO, "Can't insert a value which is in the list already"); + Q_ASSERT_X(!(n->*member).isInList(), Q_FUNC_INFO, "Can't insert a value which is in another list"); + QIntrusiveList<N, member>::insert(n); +} + +template<class N, QIntrusiveListNode N::*member> +void QScriptIntrusiveList<N, member>::remove(N *n) +{ + Q_ASSERT_X(this->contains(n), Q_FUNC_INFO, "Can't remove a value which is not in the list"); + QIntrusiveList<N, member>::remove(n); +} + +QT_END_NAMESPACE + +#endif //QSCRIPTTOOLS_P_H diff --git a/src/qml/qml/v8/qv8_p.h b/src/qml/qml/v8/qv8_p.h new file mode 100644 index 0000000000..d6a06593f5 --- /dev/null +++ b/src/qml/qml/v8/qv8_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8.h> diff --git a/src/qml/qml/v8/qv8bindings.cpp b/src/qml/qml/v8/qv8bindings.cpp new file mode 100644 index 0000000000..76fbea137e --- /dev/null +++ b/src/qml/qml/v8/qv8bindings.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8bindings_p.h" + +#include <private/qv8_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qqmlcompiler_p.h> +#include <private/qqmlproperty_p.h> +#include <private/qqmlbinding_p_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qobject_p.h> +#include <private/qqmltrace_p.h> +#include <private/qqmlprofilerservice_p.h> + +QT_BEGIN_NAMESPACE + +static QQmlJavaScriptExpression::VTable QV8Bindings_Binding_jsvtable = { + QV8Bindings::Binding::expressionIdentifier, + QV8Bindings::Binding::expressionChanged +}; + +QV8Bindings::Binding::Binding() +: QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), target(0), parent(0) +{ +} + +void QV8Bindings::Binding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + if (enabledFlag() != e) { + setEnabledFlag(e); + + if (e) update(flags); + } +} + +void QV8Bindings::refresh() +{ + int count = functions()->Length(); + for (int ii = 0; ii < count; ++ii) + bindings[ii].refresh(); +} + +void QV8Bindings::Binding::refresh() +{ + update(); +} + +int QV8Bindings::Binding::propertyIndex() const +{ + return instruction->property.encodedIndex(); +} + +QObject *QV8Bindings::Binding::object() const +{ + return target; +} + +void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) +{ + if (!enabledFlag()) + return; + + QQmlTrace trace("V8 Binding Update"); + trace.addDetail("URL", parent->url()); + trace.addDetail("Line", instruction->line); + trace.addDetail("Column", instruction->column); + + QQmlBindingProfiler prof(parent->urlString(), instruction->line, instruction->column); + + QQmlContextData *context = parent->context(); + if (!context || !context->isValid()) + return; + + if (!updatingFlag()) { + setUpdatingFlag(true); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + bool isUndefined = false; + + DeleteWatcher watcher(this); + ep->referenceScarceResources(); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local<v8::Value> result = + evaluate(context, + v8::Handle<v8::Function>::Cast(parent->functions()->Get(instruction->value)), + &isUndefined); + + trace.event("writing V8 result"); + bool needsErrorData = false; + if (!watcher.wasDeleted() && !hasError()) { + typedef QQmlPropertyPrivate PP; + needsErrorData = !PP::writeBinding(target, instruction->property, context, this, result, + isUndefined, flags); + } + + if (!watcher.wasDeleted()) { + + if (needsErrorData) { + QUrl url = parent->url(); + if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>")); + + delayedError()->error.setUrl(url); + delayedError()->error.setLine(instruction->line); + delayedError()->error.setColumn(-1); + } + + if (hasError()) { + if (!delayedError()->addError(ep)) ep->warning(delayedError()->error); + } else { + clearError(); + } + + setUpdatingFlag(false); + } + + ep->dereferenceScarceResources(); + + } else { + QQmlProperty p = QQmlPropertyPrivate::restore(target, instruction->property, + context); + QQmlBindingPrivate::printBindingLoopError(p); + } +} + +QString QV8Bindings::Binding::expressionIdentifier(QQmlJavaScriptExpression *e) +{ + Binding *This = static_cast<Binding *>(e); + return This->parent->urlString() + QLatin1String(":") + + QString::number(This->instruction->line); +} + +void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e) +{ + Binding *This = static_cast<Binding *>(e); + This->update(QQmlPropertyPrivate::DontRemoveBinding); +} + +void QV8Bindings::Binding::destroy() +{ + setEnabledFlag(false); + removeFromObject(); + clear(); + clearError(); + parent->release(); +} + +QV8Bindings::QV8Bindings(QQmlCompiledData::V8Program *program, + int line, + QQmlContextData *context) +: program(program), bindings(0), refCount(1) +{ + program->cdata->addref(); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(context->engine); + + if (program->bindings.IsEmpty()) { + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Local<v8::Script> script; + bool compileFailed = false; + { + v8::TryCatch try_catch; + const QByteArray &source = program->program; + script = engine->qmlModeCompile(source.constData(), source.length(), + program->cdata->name, line); + if (try_catch.HasCaught()) { + // The binding was not compiled. There are some exceptional cases which the + // expression rewriter does not rewrite properly (e.g., \r-terminated lines + // are not rewritten correctly but this bug is demed out-of-scope to fix for + // performance reasons; see QTBUG-24064). + compileFailed = true; + QQmlError error; + error.setDescription(QString(QLatin1String("Exception occurred during compilation of binding at line: %1")).arg(line)); + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + QQmlEnginePrivate::get(engine->engine())->warning(error); + program->bindings = qPersistentNew(v8::Array::New()); + } + } + + if (!compileFailed) { + v8::Local<v8::Value> result = script->Run(engine->contextWrapper()->sharedContext()); + if (result->IsArray()) { + program->bindings = qPersistentNew(v8::Local<v8::Array>::Cast(result)); + program->program.clear(); // We don't need the source anymore + } + } + } + + int bindingsCount = functions()->Length(); + if (bindingsCount) bindings = new QV8Bindings::Binding[bindingsCount]; + + setContext(context); +} + +QV8Bindings::~QV8Bindings() +{ + program->cdata->release(); + program = 0; + + delete [] bindings; + bindings = 0; +} + +QQmlAbstractBinding * +QV8Bindings::configBinding(QObject *target, QObject *scope, + const QQmlInstruction::instr_assignBinding *i) +{ + if (!bindings) // initialization failed. + return 0; + + QV8Bindings::Binding *rv = bindings + i->value; + + rv->instruction = i; + rv->target = target; + rv->setScopeObject(scope); + rv->setUseSharedContext(true); + rv->setNotifyOnValueChanged(true); + rv->parent = this; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +const QUrl &QV8Bindings::url() const +{ + return program->cdata->url; +} + +const QString &QV8Bindings::urlString() const +{ + return program->cdata->name; +} + +v8::Persistent<v8::Array> &QV8Bindings::functions() const +{ + return program->bindings; +} + + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8bindings_p.h b/src/qml/qml/v8/qv8bindings_p.h new file mode 100644 index 0000000000..ad5b2cb8b0 --- /dev/null +++ b/src/qml/qml/v8/qv8bindings_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8BINDINGS_P_H +#define QV8BINDINGS_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/qqmlpropertycache_p.h> +#include <private/qqmlinstruction_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlcompiler_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qflagpointer_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQmlCompiledData; + +class QV8BindingsPrivate; +class QV8Bindings : public QQmlAbstractExpression +{ +public: + QV8Bindings(QQmlCompiledData::V8Program *, + int line, + QQmlContextData *context); + virtual ~QV8Bindings(); + + QQmlAbstractBinding *configBinding(QObject *target, QObject *scope, + const QQmlInstruction::instr_assignBinding *); + + // Inherited from QQmlAbstractExpression + virtual void refresh(); + + struct Binding : public QQmlJavaScriptExpression, + public QQmlAbstractBinding { + Binding(); + + void update() { QQmlAbstractBinding::update(); } + void refresh(); + + // "Inherited" from QQmlJavaScriptExpression + static QString expressionIdentifier(QQmlJavaScriptExpression *); + static void expressionChanged(QQmlJavaScriptExpression *); + + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); + virtual void update(QQmlPropertyPrivate::WriteFlags flags); + virtual void destroy(); + virtual int propertyIndex() const; + virtual QObject *object() const; + + QObject *target; + QV8Bindings *parent; + + // To save memory, we store flags inside the instruction pointer. + // flag1: enabled + // flag2: updating + QFlagPointer<const QQmlInstruction::instr_assignBinding> instruction; + + inline bool enabledFlag() const { return instruction.flag(); } + inline void setEnabledFlag(bool v) { instruction.setFlagValue(v); } + inline bool updatingFlag() const { return instruction.flag2(); } + inline void setUpdatingFlag(bool v) { instruction.setFlag2Value(v); } + }; + + inline void addref(); + inline void release(); + +private: + Q_DISABLE_COPY(QV8Bindings) + + const QUrl &url() const; + const QString &urlString() const; + v8::Persistent<v8::Array> &functions() const; + + QQmlCompiledData::V8Program *program; + Binding *bindings; + int refCount; +}; + +void QV8Bindings::addref() +{ + ++refCount; +} + +void QV8Bindings::release() +{ + if (0 == --refCount) + delete this; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8BINDINGS_P_H + + diff --git a/src/qml/qml/v8/qv8contextwrapper.cpp b/src/qml/qml/v8/qv8contextwrapper.cpp new file mode 100644 index 0000000000..246b716aa0 --- /dev/null +++ b/src/qml/qml/v8/qv8contextwrapper.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_p.h> + +QT_BEGIN_NAMESPACE + +static QString internal(QLatin1String("You've stumbled onto an internal implementation detail " + "that should never have been exposed.")); + +class QV8ContextResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ContextType); + +public: + QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject); + ~QV8ContextResource(); + + inline QQmlContextData *getContext() const; + inline QObject *getScopeObject() const; + + quint32 isSharedContext:1; + quint32 hasSubContexts:1; + quint32 readOnly:1; + quint32 dummy:29; + + QObject *secondaryScope; + + // This is a pretty horrible hack, and an abuse of external strings. When we create a + // sub-context (a context created by a Qt.include() in an external javascript file), + // we pass a specially crafted SubContext external string as the v8::Script::Data() to + // the script, which contains a pointer to the context. We can then access the + // v8::Script::Data() later on to resolve names and URLs against the sub-context instead + // of the main outer context. + struct SubContext : public v8::String::ExternalStringResource { + SubContext(QQmlContextData *context) : context(context) {} + QQmlGuardedContextData context; + + virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); } + virtual size_t length() const { return internal.length(); } + }; + +private: + QQmlGuardedContextData context; + QQmlGuard<QObject> scopeObject; + +}; + +QV8ContextResource::QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject) +: QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true), + secondaryScope(0), context(context), scopeObject(scopeObject) +{ +} + +QV8ContextResource::~QV8ContextResource() +{ + if (context && context->isJSContext) + context->destroy(); +} + +// Returns the scope object +QObject *QV8ContextResource::getScopeObject() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedScope; + else + return scopeObject; +} + +// Returns the context, including resolving a subcontext +QQmlContextData *QV8ContextResource::getContext() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedContext; + + if (!hasSubContexts) + return context; + + v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData(); + if (callingdata.IsEmpty() || !callingdata->IsString()) + return context; + + v8::Local<v8::String> callingstring = callingdata->ToString(); + Q_ASSERT(callingstring->IsExternal()); + Q_ASSERT(callingstring->GetExternalStringResource()); + + SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource()); + return sc->context; +} + +QV8ContextWrapper::QV8ContextWrapper() +: m_engine(0) +{ +} + +QV8ContextWrapper::~QV8ContextWrapper() +{ +} + +void QV8ContextWrapper::destroy() +{ + qPersistentDispose(m_sharedContext); + qPersistentDispose(m_urlConstructor); + qPersistentDispose(m_constructor); +} + +void QV8ContextWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter); + m_urlConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::Local<v8::Object> sharedContext = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(engine, 0, 0); + r->isSharedContext = true; + sharedContext->SetExternalResource(r); + m_sharedContext = qPersistentNew<v8::Object>(sharedContext); + } +} + +v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope); + rv->SetExternalResource(r); + return rv; +} + +v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url) +{ + QQmlContextData *context = new QQmlContextData; + context->url = url; + context->isInternal = true; + context->isJSContext = true; + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_urlConstructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0); + rv->SetExternalResource(r); + return rv; +} + +void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal); + Q_ASSERT(resource); + resource->readOnly = readOnly; +} + +void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script, + QQmlContextData *ctxt) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal); + Q_ASSERT(resource); + resource->hasSubContexts = true; + script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt))); +} + +QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt); + if (!resource) return 0; + + QObject *rv = resource->secondaryScope; + resource->secondaryScope = scope; + return rv; +} + +QQmlContextData *QV8ContextWrapper::callingContext() +{ + v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal(); + if (qmlglobal.IsEmpty()) return 0; + + QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal); + return r?r->getContext():0; +} + +QQmlContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value) +{ + if (!value->IsObject()) + return 0; + + v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value); + QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal); + return r?r->getContext():0; +} + +v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + QV8Engine *engine = resource->engine; + + QString error = QLatin1String("Can't find variable: ") + engine->toString(property); + v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error))); + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle<v8::Value>(); + + // Search type (attached property/enum/imported scripts) names + // Secondary scope object + // while (context) { + // Search context properties + // Search scope object + // Search context object + // context = context->parent + // } + + QV8Engine *engine = resource->engine; + + QObject *scopeObject = resource->getScopeObject(); + + QHashedV8String propertystring(property); + + if (context->imports && QV8Engine::startsWithUpper(property)) { + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (r.scriptIndex != -1) { + int index = r.scriptIndex; + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + else + return v8::Undefined(); + } else if (r.type) { + return engine->typeWrapper()->newObject(scopeObject, r.type); + } else if (r.importNamespace) { + return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace); + } + Q_ASSERT(!"Unreachable"); + } + + // Fall through + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + if (resource->secondaryScope) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, propertystring, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) return result; + } + + while (context) { + // Search context properties + if (context->propertyNames) { + int propertyIdx = context->propertyNames->value(propertystring); + + if (propertyIdx != -1) { + + if (propertyIdx < context->idValueCount) { + + ep->captureProperty(&context->idValues[propertyIdx].bindings); + return engine->newQObject(context->idValues[propertyIdx]); + } else { + + QQmlContextPrivate *cp = context->asQQmlContextPrivate(); + + ep->captureProperty(context->asQQmlContext(), -1, + propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (value.userType() == qMetaTypeId<QList<QObject*> >()) { + QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*)propertyIdx, + 0, + QQmlContextPrivate::context_count, + QQmlContextPrivate::context_at); + return engine->listWrapper()->newList(prop, qMetaTypeId<QQmlListProperty<QObject> >()); + } else { + return engine->fromVariant(cp->propertyValues.at(propertyIdx)); + } + } + } + } + + // Search scope object + if (scopeObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + scopeObject = 0; + + + // Search context object + if (context->contextObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + QString error = QLatin1String("Can't find variable: ") + engine->toString(property); + v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error))); + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property, + v8::Local<v8::Value>, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + QV8Engine *engine = resource->engine; + + if (!resource->readOnly) { + return v8::Handle<v8::Value>(); + } else { + QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + + QLatin1String("\""); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } +} + +v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle<v8::Value>(); + + // See QV8ContextWrapper::Getter for resolution order + + QV8Engine *engine = resource->engine; + QObject *scopeObject = resource->getScopeObject(); + + QHashedV8String propertystring(property); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + // Search scope object + if (resource->secondaryScope && + qobjectWrapper->setProperty(resource->secondaryScope, propertystring, value, + QV8QObjectWrapper::IgnoreRevision)) + return value; + + while (context) { + // Search context properties + if (context->propertyNames && -1 != context->propertyNames->value(propertystring)) + return value; + + // Search scope object + if (scopeObject && + qobjectWrapper->setProperty(scopeObject, propertystring, value, QV8QObjectWrapper::CheckRevision)) + return value; + scopeObject = 0; + + // Search context object + if (context->contextObject && + qobjectWrapper->setProperty(context->contextObject, propertystring, value, + QV8QObjectWrapper::CheckRevision)) + return value; + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + if (!resource->readOnly) { + return v8::Handle<v8::Value>(); + } else { + QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + + QLatin1String("\""); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Undefined(); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8contextwrapper_p.h b/src/qml/qml/v8/qv8contextwrapper_p.h new file mode 100644 index 0000000000..117f16ab39 --- /dev/null +++ b/src/qml/qml/v8/qv8contextwrapper_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8CONTEXTWRAPPER_P_H +#define QV8CONTEXTWRAPPER_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 <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QUrl; +class QObject; +class QV8Engine; +class QQmlContextData; +class QV8ContextWrapper +{ +public: + QV8ContextWrapper(); + ~QV8ContextWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> qmlScope(QQmlContextData *ctxt, QObject *scope); + v8::Local<v8::Object> urlScope(const QUrl &); + + void setReadOnly(v8::Handle<v8::Object>, bool); + + void addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script>, + QQmlContextData *ctxt); + + // XXX We only use the secondary scope to pass the "arguments" of the signal to + // on<SignalName> properties. Instead of doing this we should rewrite the + // JavaScript closure function to accept these arguments as named parameters. + // To keep backwards compatibility we have to check that the argument names are + // not members of the QV8Engine::illegalNames() set. + QObject *setSecondaryScope(v8::Handle<v8::Object>, QObject *); + + QQmlContextData *callingContext(); + QQmlContextData *context(v8::Handle<v8::Value>); + + inline v8::Handle<v8::Object> sharedContext() const; + +private: + static v8::Handle<v8::Value> NullGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> NullSetter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_urlConstructor; + v8::Persistent<v8::Object> m_sharedContext; +}; + +v8::Handle<v8::Object> QV8ContextWrapper::sharedContext() const +{ + return m_sharedContext; +} + +QT_END_NAMESPACE + +#endif // QV8CONTEXTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8debug_p.h b/src/qml/qml/v8/qv8debug_p.h new file mode 100644 index 0000000000..4e1ec3e2ca --- /dev/null +++ b/src/qml/qml/v8/qv8debug_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8-debug.h> diff --git a/src/qml/qml/v8/qv8domerrors.cpp b/src/qml/qml/v8/qv8domerrors.cpp new file mode 100644 index 0000000000..7b8f10a27e --- /dev/null +++ b/src/qml/qml/v8/qv8domerrors.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8domerrors_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +void qt_add_domexceptions(QV8Engine *engine) +{ + // DOM Exception + v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + + v8::Local<v8::Object> domexception = v8::Object::New(); + domexception->Set(v8::String::New("INDEX_SIZE_ERR"), v8::Integer::New(DOMEXCEPTION_INDEX_SIZE_ERR), attributes); + domexception->Set(v8::String::New("DOMSTRING_SIZE_ERR"), v8::Integer::New(DOMEXCEPTION_DOMSTRING_SIZE_ERR), attributes); + domexception->Set(v8::String::New("HIERARCHY_REQUEST_ERR"), v8::Integer::New(DOMEXCEPTION_HIERARCHY_REQUEST_ERR), attributes); + domexception->Set(v8::String::New("WRONG_DOCUMENT_ERR"), v8::Integer::New(DOMEXCEPTION_WRONG_DOCUMENT_ERR), attributes); + domexception->Set(v8::String::New("INVALID_CHARACTER_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_CHARACTER_ERR), attributes); + domexception->Set(v8::String::New("NO_DATA_ALLOWED_ERR"), v8::Integer::New(DOMEXCEPTION_NO_DATA_ALLOWED_ERR), attributes); + domexception->Set(v8::String::New("NO_MODIFICATION_ALLOWED_ERR"), v8::Integer::New(DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR), attributes); + domexception->Set(v8::String::New("NOT_FOUND_ERR"), v8::Integer::New(DOMEXCEPTION_NOT_FOUND_ERR), attributes); + domexception->Set(v8::String::New("NOT_SUPPORTED_ERR"), v8::Integer::New(DOMEXCEPTION_NOT_SUPPORTED_ERR), attributes); + domexception->Set(v8::String::New("INUSE_ATTRIBUTE_ERR"), v8::Integer::New(DOMEXCEPTION_INUSE_ATTRIBUTE_ERR), attributes); + domexception->Set(v8::String::New("INVALID_STATE_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_STATE_ERR), attributes); + domexception->Set(v8::String::New("SYNTAX_ERR"), v8::Integer::New(DOMEXCEPTION_SYNTAX_ERR), attributes); + domexception->Set(v8::String::New("INVALID_MODIFICATION_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_MODIFICATION_ERR), attributes); + domexception->Set(v8::String::New("NAMESPACE_ERR"), v8::Integer::New(DOMEXCEPTION_NAMESPACE_ERR), attributes); + domexception->Set(v8::String::New("INVALID_ACCESS_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_ACCESS_ERR), attributes); + domexception->Set(v8::String::New("VALIDATION_ERR"), v8::Integer::New(DOMEXCEPTION_VALIDATION_ERR), attributes); + domexception->Set(v8::String::New("TYPE_MISMATCH_ERR"), v8::Integer::New(DOMEXCEPTION_TYPE_MISMATCH_ERR), attributes); + engine->global()->Set(v8::String::New("DOMException"), domexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8domerrors_p.h b/src/qml/qml/v8/qv8domerrors_p.h new file mode 100644 index 0000000000..5d5f277d55 --- /dev/null +++ b/src/qml/qml/v8/qv8domerrors_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8DOMERRORS_P_H +#define QV8DOMERRORS_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> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +// From DOM-Level-3-Core spec +// http://www.w3.org/TR/DOM-Level-3-Core/core.html +#define DOMEXCEPTION_INDEX_SIZE_ERR 1 +#define DOMEXCEPTION_DOMSTRING_SIZE_ERR 2 +#define DOMEXCEPTION_HIERARCHY_REQUEST_ERR 3 +#define DOMEXCEPTION_WRONG_DOCUMENT_ERR 4 +#define DOMEXCEPTION_INVALID_CHARACTER_ERR 5 +#define DOMEXCEPTION_NO_DATA_ALLOWED_ERR 6 +#define DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR 7 +#define DOMEXCEPTION_NOT_FOUND_ERR 8 +#define DOMEXCEPTION_NOT_SUPPORTED_ERR 9 +#define DOMEXCEPTION_INUSE_ATTRIBUTE_ERR 10 +#define DOMEXCEPTION_INVALID_STATE_ERR 11 +#define DOMEXCEPTION_SYNTAX_ERR 12 +#define DOMEXCEPTION_INVALID_MODIFICATION_ERR 13 +#define DOMEXCEPTION_NAMESPACE_ERR 14 +#define DOMEXCEPTION_INVALID_ACCESS_ERR 15 +#define DOMEXCEPTION_VALIDATION_ERR 16 +#define DOMEXCEPTION_TYPE_MISMATCH_ERR 17 + +#define V8THROW_DOM(error, string) { \ + v8::Local<v8::Value> v = v8::Exception::Error(v8::String::New(string)); \ + v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \ + v8::ThrowException(v); \ + return v8::Handle<v8::Value>(); \ +} +class QV8Engine; +void qt_add_domexceptions(QV8Engine *engine); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8DOMERRORS_P_H diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp new file mode 100644 index 0000000000..4c2cce1525 --- /dev/null +++ b/src/qml/qml/v8/qv8engine.cpp @@ -0,0 +1,1598 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8engine_p.h" + +#include <QtGui/QGuiApplication> + +#include "qv8contextwrapper_p.h" +#include "qv8valuetypewrapper_p.h" +#include "qv8sequencewrapper_p.h" +#include "qv8include_p.h" +#include "qjsengine_p.h" +#include "../../../3rdparty/javascriptcore/DateMath.h" + +#include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmllist_p.h> +#include <private/qqmlengine_p.h> +#include <private/qquickapplication_p.h> +#include <private/qqmlxmlhttprequest_p.h> +#include <private/qqmllocale_p.h> + +#include "qscript_impl_p.h" +#include "qv8domerrors_p.h" +#include "qv8sqlerrors_p.h" + + +Q_DECLARE_METATYPE(QJSValue) +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 + +static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs) +{ + if (lhs == rhs) + return true; + + QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource()); + QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource()); + + Q_ASSERT(lhsr->engine == rhsr->engine); + + if (lhsr && rhsr) { + QV8ObjectResource::ResourceType lhst = lhsr->resourceType(); + QV8ObjectResource::ResourceType rhst = rhsr->resourceType(); + + switch (lhst) { + case QV8ObjectResource::ValueTypeType: + // a value type might be equal to a variant or another value type + if (rhst == QV8ObjectResource::ValueTypeType) { + return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr)); + } else if (rhst == QV8ObjectResource::VariantType) { + return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr)); + } + break; + case QV8ObjectResource::VariantType: + // a variant might be equal to a value type or other variant. + if (rhst == QV8ObjectResource::VariantType) { + return lhsr->engine->variantWrapper()->toVariant(lhsr) == + lhsr->engine->variantWrapper()->toVariant(rhsr); + } else if (rhst == QV8ObjectResource::ValueTypeType) { + return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr)); + } + break; + case QV8ObjectResource::SequenceType: + // a sequence might be equal to itself. + if (rhst == QV8ObjectResource::SequenceType) { + return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr); + } + break; + default: + break; + } + } + + return false; +} + + +QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) + : q(qq) + , m_engine(0) + , m_ownsV8Context(ownership == QJSEngine::CreateNewContext) + , m_xmlHttpRequestData(0) + , m_listModelData(0) +{ + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + QByteArray v8args = qgetenv("V8ARGS"); + // change default v8 behaviour to not relocate breakpoints across lines + if (!v8args.contains("breakpoint_relocation")) + v8args.append(" --nobreakpoint_relocation"); + v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); + + ensurePerThreadIsolate(); + + v8::HandleScope handle_scope; + m_context = (ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); + qPersistentRegister(m_context); + m_originalGlobalObject.init(m_context); + v8::Context::Scope context_scope(m_context); + + v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); + QV8GCCallback::registerGcPrologueCallback(); + m_strongReferencer = qPersistentNew(v8::Object::New()); + + m_stringWrapper.init(); + m_contextWrapper.init(this); + m_qobjectWrapper.init(this); + m_typeWrapper.init(this); + m_listWrapper.init(this); + m_variantWrapper.init(this); + m_valueTypeWrapper.init(this); + m_sequenceWrapper.init(this); + + { + v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); + m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v)); + } +} + +QV8Engine::~QV8Engine() +{ + Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited"); + for (int ii = 0; ii < m_extensionData.count(); ++ii) + delete m_extensionData[ii]; + m_extensionData.clear(); + + qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); + m_xmlHttpRequestData = 0; + delete m_listModelData; + m_listModelData = 0; + + qPersistentDispose(m_freezeObject); + qPersistentDispose(m_getOwnPropertyNames); + + invalidateAllValues(); + clearExceptions(); + + qPersistentDispose(m_strongReferencer); + + m_sequenceWrapper.destroy(); + m_valueTypeWrapper.destroy(); + m_variantWrapper.destroy(); + m_listWrapper.destroy(); + m_typeWrapper.destroy(); + m_qobjectWrapper.destroy(); + m_contextWrapper.destroy(); + m_stringWrapper.destroy(); + + m_originalGlobalObject.destroy(); + + if (m_ownsV8Context) + qPersistentDispose(m_context); +} + +QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr) +{ + return toStringStatic(jsstr->ToString()); +} + +QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr) +{ + QString qstr; + qstr.resize(jsstr->Length()); + jsstr->Write((uint16_t*)qstr.data()); + return qstr; +} + +QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) +{ + if (value.IsEmpty()) + return QVariant(); + + if (typeHint == QVariant::Bool) + return QVariant(value->BooleanValue()); + + if (value->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); + if (r) { + switch (r->resourceType()) { + case QV8ObjectResource::Context2DStyleType: + case QV8ObjectResource::Context2DPixelArrayType: + case QV8ObjectResource::SignalHandlerType: + case QV8ObjectResource::IncubatorType: + case QV8ObjectResource::VisualDataItemType: + case QV8ObjectResource::ContextType: + case QV8ObjectResource::XMLHttpRequestType: + case QV8ObjectResource::DOMNodeType: + case QV8ObjectResource::SQLDatabaseType: + case QV8ObjectResource::ListModelType: + case QV8ObjectResource::Context2DType: + case QV8ObjectResource::ParticleDataType: + case QV8ObjectResource::LocaleDataType: + return QVariant(); + case QV8ObjectResource::TypeType: + return m_typeWrapper.toVariant(r); + case QV8ObjectResource::QObjectType: + return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r)); + case QV8ObjectResource::ListType: + return m_listWrapper.toVariant(r); + case QV8ObjectResource::VariantType: + return m_variantWrapper.toVariant(r); + case QV8ObjectResource::ValueTypeType: + return m_valueTypeWrapper.toVariant(r); + case QV8ObjectResource::SequenceType: + return m_sequenceWrapper.toVariant(r); + } + } + } + + if (value->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + if (typeHint == qMetaTypeId<QList<QObject *> >()) { + QList<QObject *> list; + uint32_t length = array->Length(); + for (uint32_t ii = 0; ii < length; ++ii) { + v8::Local<v8::Value> arrayItem = array->Get(ii); + if (arrayItem->IsObject()) { + list << toQObject(arrayItem->ToObject()); + } else { + list << 0; + } + } + + return qVariantFromValue<QList<QObject*> >(list); + } + + bool succeeded = false; + QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded); + if (succeeded) + return retn; + } + + return toBasicVariant(value); +} + +static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Array> result = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + result->Set(ii, engine->toString(list.at(ii))); + return result; +} + +static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Array> result = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + result->Set(ii, engine->fromVariant(list.at(ii))); + return result; +} + +static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Object> object = v8::Object::New(); + for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter) + object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value())); + return object; +} + +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) +{ + int type = variant.userType(); + const void *ptr = variant.constData(); + + if (type < QMetaType::User) { + switch (QMetaType::Type(type)) { + case QMetaType::Void: + return v8::Undefined(); + case QMetaType::Bool: + return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr)); + case QMetaType::Int: + return v8::Integer::New(*reinterpret_cast<const int*>(ptr)); + case QMetaType::UInt: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr)); + case QMetaType::LongLong: + return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr)); + case QMetaType::ULongLong: + return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr)); + case QMetaType::Double: + return v8::Number::New(*reinterpret_cast<const double*>(ptr)); + case QMetaType::QString: + return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr)); + case QMetaType::Float: + return v8::Number::New(*reinterpret_cast<const float*>(ptr)); + case QMetaType::Short: + return v8::Integer::New(*reinterpret_cast<const short*>(ptr)); + case QMetaType::UShort: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr)); + case QMetaType::Char: + return v8::Integer::New(*reinterpret_cast<const char*>(ptr)); + case QMetaType::UChar: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr)); + case QMetaType::QChar: + return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode()); + case QMetaType::QDateTime: + return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr))); + case QMetaType::QDate: + return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr)))); + case QMetaType::QTime: + return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr)))); + case QMetaType::QRegExp: + return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr)); + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return newQObject(*reinterpret_cast<QObject* const *>(ptr)); + case QMetaType::QStringList: + { + bool succeeded = false; + v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); + if (succeeded) + return retn; + return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr)); + } + case QMetaType::QVariantList: + return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); + case QMetaType::QVariantMap: + return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); + + default: + break; + } + + if (m_engine) { + if (QQmlValueType *vt = QQmlEnginePrivate::get(m_engine)->valueTypes[type]) + return m_valueTypeWrapper.newValueType(variant, vt); + } + + } else { + if (type == qMetaTypeId<QQmlListReference>()) { + typedef QQmlListReferencePrivate QDLRP; + QDLRP *p = QDLRP::get((QQmlListReference*)ptr); + if (p->object) { + return m_listWrapper.newList(p->property, p->propertyType); + } else { + return v8::Null(); + } + } else if (type == qMetaTypeId<QJSValue>()) { + const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr); + QJSValuePrivate *valuep = QJSValuePrivate::get(*value); + if (valuep->assignEngine(this)) + return v8::Local<v8::Value>::New(*valuep); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + const QList<QObject *> &list = *(QList<QObject *>*)ptr; + v8::Local<v8::Array> array = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + array->Set(ii, newQObject(list.at(ii))); + return array; + } + + bool objOk; + QObject *obj = QQmlMetaType::toQObject(variant, &objOk); + if (objOk) + return newQObject(obj); + + bool succeeded = false; + v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); + if (succeeded) + return retn; + } + + // XXX TODO: To be compatible, we still need to handle: + // + QObjectList + // + QList<int> + + return m_variantWrapper.newVariant(variant); +} + +// A handle scope and context must be entered +v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, + const QString &fileName, + int lineNumber) +{ + v8::Local<v8::String> v8source = m_stringWrapper.toString(source); + v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); + + v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); + + v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), + v8::Script::QmlMode); + + return script; +} + +// A handle scope and context must be entered. +// source can be either ascii or utf8. +v8::Local<v8::Script> QV8Engine::qmlModeCompile(const char *source, int sourceLength, + const QString &fileName, + int lineNumber) +{ + if (sourceLength == -1) + sourceLength = strlen(source); + + v8::Local<v8::String> v8source = v8::String::New(source, sourceLength); + v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); + + v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); + + v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), + v8::Script::QmlMode); + + return script; +} + +QNetworkAccessManager *QV8Engine::networkAccessManager() +{ + return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); +} + +const QStringHash<bool> &QV8Engine::illegalNames() const +{ + return m_illegalNames; +} + +// Requires a handle scope +v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o) +{ + // FIXME Newer v8 have API for this function + v8::TryCatch tc; + v8::Handle<v8::Value> args[] = { o }; + v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args); + if (tc.HasCaught()) + return v8::Array::New(); + else + return v8::Local<v8::Array>::Cast(r); +} + +QQmlContextData *QV8Engine::callingContext() +{ + return m_contextWrapper.callingContext(); +} + +// Converts a JS value to a QVariant. +// Null, Undefined -> QVariant() (invalid) +// Boolean -> QVariant(bool) +// Number -> QVariant(double) +// String -> QVariant(QString) +// Array -> QVariantList(...) +// Date -> QVariant(QDateTime) +// RegExp -> QVariant(QRegExp) +// [Any other object] -> QVariantMap(...) +QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value) +{ + if (value->IsNull() || value->IsUndefined()) + return QVariant(); + if (value->IsBoolean()) + return value->ToBoolean()->Value(); + if (value->IsInt32()) + return value->ToInt32()->Value(); + if (value->IsNumber()) + return value->ToNumber()->Value(); + if (value->IsString()) + return m_stringWrapper.toString(value->ToString()); + if (value->IsDate()) + return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue()); + // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! + + Q_ASSERT(value->IsObject()); + + if (value->IsRegExp()) { + v8::Context::Scope scope(context()); + return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + } + if (value->IsArray()) { + v8::Context::Scope scope(context()); + QVariantList rv; + + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + int length = array->Length(); + for (int ii = 0; ii < length; ++ii) + rv << toVariant(array->Get(ii), -1); + return rv; + } + if (!value->IsFunction()) { + v8::Context::Scope scope(context()); + v8::Handle<v8::Object> object = value->ToObject(); + return variantMapFromJS(object); + } + + return QVariant(); +} + + + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) +{ + using namespace QQmlBuiltinFunctions; + + v8::Local<v8::Object> console = v8::Object::New(); + v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this); + + console->Set(v8::String::New("debug"), consoleLogFn); + console->Set(v8::String::New("log"), consoleLogFn); + console->Set(v8::String::New("info"), consoleLogFn); + console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this)); + console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this)); + console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this)); + + console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this)); + console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this)); + console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this)); + console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this)); + console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this)); + console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this)); + console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this)); + + v8::Local<v8::Object> qt = v8::Object::New(); + + // Set all the enums from the "Qt" namespace + const QMetaObject *qtMetaObject = StaticQtMetaObject::get(); + for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) { + QMetaEnum enumerator = qtMetaObject->enumerator(ii); + for (int jj = 0; jj < enumerator.keyCount(); ++jj) { + qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj))); + } + } + qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0)); + qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1)); + + qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this)); + qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this)); + qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this)); + qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this)); + qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this)); + qt->Set(v8::String::New("point"), V8FUNCTION(point, this)); + qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); + qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); + qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this)); + + qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this)); + qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this)); + qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this)); + + qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this)); + qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this)); + qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this)); + qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this)); + qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this)); + qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); + qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this)); + + if (m_engine) { + qt->Set(v8::String::New("application"), newQObject(new QQuickApplication(m_engine))); + qt->Set(v8::String::New("inputMethod"), newQObject(qGuiApp->inputMethod(), CppOwnership)); + qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); + qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); + qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); + qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this)); + qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); + qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); + } + + global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this)); + global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this)); + global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this)); + global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this)); + global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this)); + global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this)); + + global->Set(v8::String::New("print"), consoleLogFn); + global->Set(v8::String::New("console"), console); + global->Set(v8::String::New("Qt"), qt); + global->Set(v8::String::New("gc"), V8FUNCTION(QQmlBuiltinFunctions::gc, this)); + + { +#define STRING_ARG "(function(stringArg) { "\ + " String.prototype.arg = (function() {"\ + " return stringArg.apply(this, arguments);"\ + " })"\ + "})" + + v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode); + v8::Local<v8::Value> result = registerArg->Run(); + Q_ASSERT(result->IsFunction()); + v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result); + v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this); + registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args); +#undef STRING_ARG + } + + QQmlLocale::registerStringLocaleCompare(this); + QQmlDateExtension::registerExtension(this); + QQmlNumberExtension::registerExtension(this); + + qt_add_domexceptions(this); + m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); + + qt_add_sqlexceptions(this); + + { + v8::Handle<v8::Value> args[] = { global }; + v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args); + v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names); + for (quint32 ii = 0; ii < namesArray->Length(); ++ii) + m_illegalNames.insert(toString(namesArray->Get(ii)), true); + } + + { +#define FREEZE_SOURCE "(function freeze_recur(obj) { "\ + " if (Qt.isQtObject(obj)) return;"\ + " if (obj != Function.connect && obj != Function.disconnect && "\ + " obj instanceof Object) {"\ + " var properties = Object.getOwnPropertyNames(obj);"\ + " for (var prop in properties) { "\ + " if (prop == \"connect\" || prop == \"disconnect\") {"\ + " Object.freeze(obj[prop]); "\ + " continue;"\ + " }"\ + " freeze_recur(obj[prop]);"\ + " }"\ + " }"\ + " if (obj instanceof Object) {"\ + " Object.freeze(obj);"\ + " }"\ + "})" + + v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE)); + v8::Local<v8::Value> result = freeze->Run(); + Q_ASSERT(result->IsFunction()); + m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result)); +#undef FREEZE_SOURCE + } +} + +void QV8Engine::freezeObject(v8::Handle<v8::Value> value) +{ + v8::Handle<v8::Value> args[] = { value }; + m_freezeObject->Call(global(), 1, args); +} + +void QV8Engine::gc() +{ + v8::V8::LowMemoryNotification(); + while (!v8::V8::IdleNotification()) {} +} + +#ifdef QML_GLOBAL_HANDLE_DEBUGGING +#include <QtCore/qthreadstorage.h> +static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles; + +void QV8Engine::registerHandle(void *handle) +{ + if (!handle) { + qWarning("Attempting to register a null handle"); + return; + } + + if (!QV8Engine_activeHandles.hasLocalData()) + QV8Engine_activeHandles.setLocalData(new QSet<void *>); + + if (QV8Engine_activeHandles.localData()->contains(handle)) { + qFatal("Handle %p already alive", handle); + } else { + QV8Engine_activeHandles.localData()->insert(handle); + } +} + +void QV8Engine::releaseHandle(void *handle) +{ + if (!handle) + return; + + if (!QV8Engine_activeHandles.hasLocalData()) + QV8Engine_activeHandles.setLocalData(new QSet<void *>); + + if (QV8Engine_activeHandles.localData()->contains(handle)) { + QV8Engine_activeHandles.localData()->remove(handle); + } else { + qFatal("Handle %p already dead", handle); + } +} +#endif + +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; +} + +double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt) +{ + // from QScriptEngine::DateTimeToMs() + if (!dt.isValid()) { + return qSNaN(); + } + QDateTime utc = dt.toUTC(); + QDate date = utc.date(); + QTime time = utc.time(); + QV8DateConverter::JSC::GregorianDateTime tm; + tm.year = date.year() - 1900; + tm.month = date.month() - 1; + tm.monthDay = date.day(); + tm.weekDay = date.dayOfWeek(); + tm.yearDay = date.dayOfYear(); + tm.hour = time.hour(); + tm.minute = time.minute(); + tm.second = time.second(); + return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec()); +} + +v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong) +{ + QObject *parent = object->parent(); + if (!parent) { + // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value. + if (QQmlEngine::objectOwnership(object) == QQmlEngine::JavaScriptOwnership) { + *shouldBeStrong = false; + return &(QQmlData::get(object)->v8object); + } + + // no parent, and has CPP ownership - doesn't have an implicit parent. + *shouldBeStrong = true; + return 0; + } + + // if it is owned by CPP, it's root parent may still be owned by JS. + // in that case, the owner of the persistent handle is the root parent's v8object. + while (parent->parent()) + parent = parent->parent(); + + if (QQmlEngine::objectOwnership(parent) == QQmlEngine::JavaScriptOwnership) { + // root parent is owned by JS. It's v8object owns the persistent value in question. + *shouldBeStrong = false; + return &(QQmlData::get(parent)->v8object); + } else { + // root parent has CPP ownership. The persistent value should not be made weak. + *shouldBeStrong = true; + return 0; + } +} + +QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) +{ + // from QScriptEngine::MsToDateTime() + if (qIsNaN(jsDate)) + return QDateTime(); + QV8DateConverter::JSC::GregorianDateTime tm; + QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm); + + // from QScriptEngine::MsFromTime() + int ms = int(::fmod(jsDate, 1000.0)); + if (ms < 0) + ms += int(1000.0); + + QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), + QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); + return convertedUTC.toLocalTime(); +} + +void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle) +{ + if (handle.IsEmpty()) + return; + + bool handleShouldBeStrong = false; + v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); + if (handleShouldBeStrong) { + v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); + } else if (!implicitOwner->IsEmpty()) { + v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); + } +} + +void QV8Engine::addRelationshipForGC(QObject *object, QObject *other) +{ + bool handleShouldBeStrong = false; + v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); + v8::Persistent<v8::Value> handle = QQmlData::get(other, true)->v8object; + if (handleShouldBeStrong) { + v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); + } else if (!implicitOwner->IsEmpty()) { + v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); + } +} + +static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData; + +bool QV8Engine::hasThreadData() +{ + return perThreadEngineData.hasLocalData(); +} + +QV8Engine::ThreadData *QV8Engine::threadData() +{ + Q_ASSERT(perThreadEngineData.hasLocalData()); + return perThreadEngineData.localData(); +} + +void QV8Engine::ensurePerThreadIsolate() +{ + if (!perThreadEngineData.hasLocalData()) + perThreadEngineData.setLocalData(new ThreadData); +} + +void QV8Engine::initDeclarativeGlobalObject() +{ + v8::HandleScope handels; + v8::Context::Scope contextScope(m_context); + initializeGlobal(m_context->Global()); + freezeObject(m_context->Global()); +} + +void QV8Engine::setEngine(QQmlEngine *engine) +{ + m_engine = engine; + initDeclarativeGlobalObject(); +} + +void QV8Engine::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg) +{ + m_exception.set(value, msg); +} + +v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value) +{ + setException(value); + v8::ThrowException(value); + return value; +} + +void QV8Engine::clearExceptions() +{ + m_exception.clear(); +} + +v8::Handle<v8::Value> QV8Engine::uncaughtException() const +{ + if (!hasUncaughtException()) + return v8::Handle<v8::Value>(); + return m_exception; +} + +bool QV8Engine::hasUncaughtException() const +{ + return m_exception; +} + +int QV8Engine::uncaughtExceptionLineNumber() const +{ + return m_exception.lineNumber(); +} + +QStringList QV8Engine::uncaughtExceptionBacktrace() const +{ + return m_exception.backtrace(); +} + +/*! + \internal + Save the current exception on stack so it can be set again later. + \sa QV8Engine::restoreException +*/ +void QV8Engine::saveException() +{ + m_exception.push(); +} + +/*! + \internal + Load a saved exception from stack. Current exception, if exists will be dropped + \sa QV8Engine::saveException +*/ +void QV8Engine::restoreException() +{ + m_exception.pop(); +} + +QV8Engine::Exception::Exception() {} + +QV8Engine::Exception::~Exception() +{ + Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found."); + clear(); +} + +void QV8Engine::Exception::set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message) +{ + Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected"); + clear(); + m_value = v8::Persistent<v8::Value>::New(value); + m_message = v8::Persistent<v8::Message>::New(message); +} + +void QV8Engine::Exception::clear() +{ + m_value.Dispose(); + m_value.Clear(); + m_message.Dispose(); + m_message.Clear(); +} + +QV8Engine::Exception::operator bool() const +{ + return !m_value.IsEmpty(); +} + +QV8Engine::Exception::operator v8::Handle<v8::Value>() const +{ + Q_ASSERT(*this); + return m_value; +} + +int QV8Engine::Exception::lineNumber() const +{ + if (m_message.IsEmpty()) + return -1; + return m_message->GetLineNumber(); +} + +QStringList QV8Engine::Exception::backtrace() const +{ + if (m_message.IsEmpty()) + return QStringList(); + + QStringList backtrace; + v8::Handle<v8::StackTrace> trace = m_message->GetStackTrace(); + if (trace.IsEmpty()) + // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called). + return QStringList(); + + for (int i = 0; i < trace->GetFrameCount(); ++i) { + v8::Local<v8::StackFrame> frame = trace->GetFrame(i); + backtrace.append(QJSConverter::toString(frame->GetFunctionName())); + backtrace.append(QJSConverter::toString(frame->GetFunctionName())); + backtrace.append(QString::fromAscii("()@")); + backtrace.append(QJSConverter::toString(frame->GetScriptName())); + backtrace.append(QString::fromAscii(":")); + backtrace.append(QString::number(frame->GetLineNumber())); + } + return backtrace; +} + +void QV8Engine::Exception::push() +{ + m_stack.push(qMakePair(m_value, m_message)); + m_value.Clear(); + m_message.Clear(); +} + +void QV8Engine::Exception::pop() +{ + Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found"); + ValueMessagePair pair = m_stack.pop(); + clear(); + m_value = pair.first; + m_message = pair.second; +} + + +// Converts a QVariantList to JS. +// The result is a new Array object with length equal to the length +// of the QVariantList, and the elements being the QVariantList's +// elements converted to JS, recursively. +v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst) +{ + v8::Local<v8::Array> result = v8::Array::New(lst.size()); + for (int i = 0; i < lst.size(); ++i) + result->Set(i, variantToJS(lst.at(i))); + return result; +} + +// Converts a JS Array object to a QVariantList. +// The result is a QVariantList with length equal to the length +// of the JS Array, and elements being the JS Array's elements +// converted to QVariants, recursively. +QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray) +{ + QVariantList result; + int hash = jsArray->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + v8::HandleScope handleScope; + visitedConversionObjects.insert(hash); + uint32_t length = jsArray->Length(); + for (uint32_t i = 0; i < length; ++i) + result.append(variantFromJS(jsArray->Get(i))); + visitedConversionObjects.remove(hash); + return result; +} + +// Converts a QVariantMap to JS. +// The result is a new Object object with property names being +// the keys of the QVariantMap, and values being the values of +// the QVariantMap converted to JS, recursively. +v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap) +{ + v8::Local<v8::Object> result = v8::Object::New(); + QVariantMap::const_iterator it; + for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) + result->Set(QJSConverter::toString(it.key()), variantToJS(it.value())); + return result; +} + +// Converts a JS Object to a QVariantMap. +// The result is a QVariantMap with keys being the property names +// of the object, and values being the values of the JS object's +// properties converted to QVariants, recursively. +QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject) +{ + QVariantMap result; + + v8::HandleScope handleScope; + v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames(); + uint32_t length = propertyNames->Length(); + if (length == 0) + return result; + + int hash = jsObject->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + + visitedConversionObjects.insert(hash); + // TODO: Only object's own property names. Include non-enumerable properties. + for (uint32_t i = 0; i < length; ++i) { + v8::Handle<v8::Value> name = propertyNames->Get(i); + result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name))); + } + visitedConversionObjects.remove(hash); + return result; +} + +// Converts the meta-type defined by the given type and data to JS. +// Returns the value if conversion succeeded, an empty handle otherwise. +v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data) +{ + Q_ASSERT(data != 0); + v8::Handle<v8::Value> result; + + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Void: + return v8::Undefined(); + case QMetaType::Bool: + return v8::Boolean::New(*reinterpret_cast<const bool*>(data)); + case QMetaType::Int: + return v8::Int32::New(*reinterpret_cast<const int*>(data)); + case QMetaType::UInt: + return v8::Uint32::New(*reinterpret_cast<const uint*>(data)); + case QMetaType::LongLong: + return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data))); + case QMetaType::ULongLong: +#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 +#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") + return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); +#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); +#else + return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data))); +#endif + case QMetaType::Double: + return v8::Number::New(double(*reinterpret_cast<const double*>(data))); + case QMetaType::QString: + return QJSConverter::toString(*reinterpret_cast<const QString*>(data)); + case QMetaType::Float: + return v8::Number::New(*reinterpret_cast<const float*>(data)); + case QMetaType::Short: + return v8::Int32::New(*reinterpret_cast<const short*>(data)); + case QMetaType::UShort: + return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data)); + case QMetaType::Char: + return v8::Int32::New(*reinterpret_cast<const char*>(data)); + case QMetaType::UChar: + return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data)); + case QMetaType::QChar: + return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode()); + case QMetaType::QStringList: + result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data)); + break; + case QMetaType::QVariantList: + result = variantListToJS(*reinterpret_cast<const QVariantList *>(data)); + break; + case QMetaType::QVariantMap: + result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data)); + break; + case QMetaType::QDateTime: + result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data)); + break; + case QMetaType::QDate: + result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data))); + break; + case QMetaType::QRegExp: + result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data)); + break; + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + result = newQObject(*reinterpret_cast<QObject* const *>(data)); + break; + case QMetaType::QVariant: + result = variantToJS(*reinterpret_cast<const QVariant*>(data)); + break; + default: + if (type == qMetaTypeId<QJSValue>()) { + return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this); + } else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) { + return v8::Null(); + } else { + // Fall back to wrapping in a QVariant. + result = newVariant(QVariant(type, data)); + } + } + } + return result; +} + +// Converts a JS value to a meta-type. +// data must point to a place that can store a value of the given type. +// Returns true if conversion succeeded, false otherwise. +bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) { + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value(); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(data) = value->ToInt32()->Value(); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(data) = value->ToUint32()->Value(); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value()); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value()); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(data) = value->ToNumber()->Value(); + return true; + case QMetaType::QString: + if (value->IsUndefined() || value->IsNull()) + *reinterpret_cast<QString*>(data) = QString(); + else + *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString()); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(data) = value->ToNumber()->Value(); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value()); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16() + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value()); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value()); + return true; + case QMetaType::QChar: + if (value->IsString()) { + QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value)); + *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); + } else { + *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16() + } + return true; + case QMetaType::QDateTime: + if (value->IsDate()) { + *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); + return true; + } break; + case QMetaType::QDate: + if (value->IsDate()) { + *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date(); + return true; + } break; + case QMetaType::QRegExp: + if (value->IsRegExp()) { + *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + return true; + } break; + case QMetaType::QObjectStar: + if (isQObject(value) || value->IsNull()) { + *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value); + return true; + } break; + case QMetaType::QWidgetStar: + if (isQObject(value) || value->IsNull()) { + QObject *qo = qtObjectFromJS(value); + if (!qo || qo->isWidgetType()) { + *reinterpret_cast<QWidget* *>(data) = reinterpret_cast<QWidget*>(qo); + return true; + } + } break; + case QMetaType::QStringList: + if (value->IsArray()) { + *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value)); + return true; + } break; + case QMetaType::QVariantList: + if (value->IsArray()) { + *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value)); + return true; + } break; + case QMetaType::QVariantMap: + if (value->IsObject()) { + *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value)); + return true; + } break; + case QMetaType::QVariant: + *reinterpret_cast<QVariant*>(data) = variantFromJS(value); + return true; + default: + ; + } + +#if 0 + if (isQtVariant(value)) { + const QVariant &var = variantValue(value); + // ### Enable once constructInPlace() is in qt master. + if (var.userType() == type) { + QMetaType::constructInPlace(type, data, var.constData()); + return true; + } + if (var.canConvert(QVariant::Type(type))) { + QVariant vv = var; + vv.convert(QVariant::Type(type)); + Q_ASSERT(vv.userType() == type); + QMetaType::constructInPlace(type, data, vv.constData()); + return true; + } + + } +#endif + + // Try to use magic; for compatibility with qscriptvalue_cast. + + QByteArray name = QMetaType::typeName(type); + if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data))) + return true; + if (isVariant(value) && name.endsWith('*')) { + int valueType = QMetaType::type(name.left(name.size()-1)); + QVariant &var = variantValue(value); + if (valueType == var.userType()) { + // We have T t, T* is requested, so return &t. + *reinterpret_cast<void* *>(data) = var.data(); + return true; + } else { + // Look in the prototype chain. + v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype(); + while (proto->IsObject()) { + bool canCast = false; + if (isVariant(proto)) { + canCast = (type == variantValue(proto).userType()) + || (valueType && (valueType == variantValue(proto).userType())); + } + else if (isQObject(proto)) { + QByteArray className = name.left(name.size()-1); + if (QObject *qobject = qtObjectFromJS(proto)) + canCast = qobject->qt_metacast(className) != 0; + } + if (canCast) { + QByteArray varTypeName = QMetaType::typeName(var.userType()); + if (varTypeName.endsWith('*')) + *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data()); + else + *reinterpret_cast<void* *>(data) = var.data(); + return true; + } + proto = proto->ToObject()->GetPrototype(); + } + } + } else if (value->IsNull() && name.endsWith('*')) { + *reinterpret_cast<void* *>(data) = 0; + return true; + } else if (type == qMetaTypeId<QJSValue>()) { + *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value)); + return true; + } + + return false; +} + +// Converts a QVariant to JS. +v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value) +{ + return metaTypeToJS(value.userType(), value.constData()); +} + +// Converts a JS value to a QVariant. +// Null, Undefined -> QVariant() (invalid) +// Boolean -> QVariant(bool) +// Number -> QVariant(double) +// String -> QVariant(QString) +// Array -> QVariantList(...) +// Date -> QVariant(QDateTime) +// RegExp -> QVariant(QRegExp) +// [Any other object] -> QVariantMap(...) +QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value) +{ + Q_ASSERT(!value.IsEmpty()); + if (value->IsNull() || value->IsUndefined()) + return QVariant(); + if (value->IsBoolean()) + return value->ToBoolean()->Value(); + if (value->IsInt32()) + return value->ToInt32()->Value(); + if (value->IsNumber()) + return value->ToNumber()->Value(); + if (value->IsString()) + return QJSConverter::toString(value->ToString()); + Q_ASSERT(value->IsObject()); + if (value->IsArray()) + return variantListFromJS(v8::Handle<v8::Array>::Cast(value)); + if (value->IsDate()) + return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); + if (value->IsRegExp()) + return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + if (isVariant(value)) + return variantValue(value); + if (isQObject(value)) + return qVariantFromValue(qtObjectFromJS(value)); + return variantMapFromJS(value->ToObject()); +} + +bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value, + const QByteArray &targetType, + void **result) +{ + if (!targetType.endsWith('*')) + return false; + if (QObject *qobject = qtObjectFromJS(value)) { + int start = targetType.startsWith("const ") ? 6 : 0; + QByteArray className = targetType.mid(start, targetType.size()-start-1); + if (void *instance = qobject->qt_metacast(className)) { + *result = instance; + return true; + } + } + return false; +} + +QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value) +{ + if (!value->IsObject()) + return 0; + + QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); + if (!r) + return 0; + QV8ObjectResource::ResourceType type = r->resourceType(); + if (type == QV8ObjectResource::QObjectType) + return qobjectWrapper()->toQObject(r); + else if (type == QV8ObjectResource::VariantType) { + QVariant variant = variantWrapper()->toVariant(r); + int type = variant.userType(); + if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar)) + return *reinterpret_cast<QObject* const *>(variant.constData()); + } + return 0; +} + + +QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value) +{ + return variantWrapper()->variantValue(value); +} + +// Creates a QVariant wrapper object. +v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value) +{ + return variantWrapper()->newVariant(value); +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch) +{ + v8::HandleScope handleScope; + + clearExceptions(); + if (script.IsEmpty()) { + v8::Handle<v8::Value> exception = tryCatch.Exception(); + if (exception.IsEmpty()) { + // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression. + return new QJSValuePrivate(this); + } + setException(exception, tryCatch.Message()); + return new QJSValuePrivate(this, exception); + } + v8::Handle<v8::Value> result; + result = script->Run(); + if (result.IsEmpty()) { + v8::Handle<v8::Value> exception = tryCatch.Exception(); + // TODO: figure out why v8 doesn't always produce an exception value + //Q_ASSERT(!exception.IsEmpty()); + if (exception.IsEmpty()) + exception = v8::Exception::Error(v8::String::New("missing exception value")); + setException(exception, tryCatch.Message()); + return new QJSValuePrivate(this, exception); + } + return new QJSValuePrivate(this, result); +} + +QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const +{ + if (value.IsEmpty()) + return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this))); + return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value)); +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length) +{ + return new QJSValuePrivate(this, v8::Array::New(length)); +} + +void QV8Engine::emitSignalHandlerException() +{ + emit q->signalHandlerException(scriptValueFromInternal(uncaughtException())); +} + +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, int line, int 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; +} + +void QV8GCCallback::registerGcPrologueCallback() +{ + QV8Engine::ThreadData *td = QV8Engine::threadData(); + if (!td->gcPrologueCallbackRegistered) { + td->gcPrologueCallbackRegistered = true; + v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact); + } +} + +QV8GCCallback::Node::Node(PrologueCallback callback) + : prologueCallback(callback) +{ +} + +QV8GCCallback::Node::~Node() +{ + node.remove(); +} + +/* + Ensure that each persistent handle is strong if it has CPP ownership + and has no implicitly JS owned object owner in its parent chain, and + weak otherwise. + + Any weak handle whose parent object is still alive will have an implicit + reference (between the parent and the handle) added, so that it will + not be collected. + + Note that this callback is registered only for kGCTypeMarkSweepCompact + collection cycles, as it is during collection cycles of that type + in which weak persistent handle callbacks are called when required. + */ +void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags) +{ + if (!QV8Engine::hasThreadData()) + return; + + QV8Engine::ThreadData *td = QV8Engine::threadData(); + QV8GCCallback::Node *currNode = td->gcCallbackNodes.first(); + + while (currNode) { + // The client which adds itself to the list is responsible + // for maintaining the correct implicit references in the + // specified callback. + currNode->prologueCallback(currNode); + currNode = td->gcCallbackNodes.next(currNode); + } +} + +void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node) +{ + QV8Engine::ThreadData *td = QV8Engine::threadData(); + td->gcCallbackNodes.insert(node); +} + +QV8Engine::ThreadData::ThreadData() + : gcPrologueCallbackRegistered(false) +{ + if (!v8::Isolate::GetCurrent()) { + isolate = v8::Isolate::New(); + isolate->Enter(); + } else { + isolate = 0; + } +} + +QV8Engine::ThreadData::~ThreadData() +{ + if (isolate) { + isolate->Exit(); + isolate->Dispose(); + isolate = 0; + } +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8engine_impl_p.h b/src/qml/qml/v8/qv8engine_impl_p.h new file mode 100644 index 0000000000..ebb21f851c --- /dev/null +++ b/src/qml/qml/v8/qv8engine_impl_p.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8ENGINE_IMPL_P_H +#define QV8ENGINE_IMPL_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 "qv8engine_p.h" +#include "qjsvalue_p.h" +#include "qjsconverter_p.h" +#include "qjsvalueiterator_p.h" + +QT_BEGIN_NAMESPACE + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(bool value) +{ + return value ? v8::True() : v8::False(); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(int value) +{ + return v8::Integer::New(value); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(uint value) +{ + return v8::Integer::NewFromUnsigned(value); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(double value) +{ + return v8::Number::New(value); +} + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(QJSValue::SpecialValue value) { + if (value == QJSValue::NullValue) + return v8::Null(); + return v8::Undefined(); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(const QString &value) +{ + return QJSConverter::toString(value); +} + +class QtScriptBagCleaner +{ +public: + template<class T> + void operator () (T* value) const + { + value->reinitialize(); + } + void operator () (QJSValueIteratorPrivate *iterator) const + { + iterator->invalidate(); + } +}; + +inline void QV8Engine::registerValue(QJSValuePrivate *data) +{ + m_values.insert(data); +} + +inline void QV8Engine::unregisterValue(QJSValuePrivate *data) +{ + m_values.remove(data); +} + +inline void QV8Engine::invalidateAllValues() +{ + ValueList::iterator it; + for (it = m_values.begin(); it != m_values.end(); it = it.erase()) + (*it)->invalidate(); + Q_ASSERT(m_values.isEmpty()); +} + +inline void QV8Engine::registerValueIterator(QJSValueIteratorPrivate *data) +{ + m_valueIterators.insert(data); +} + +inline void QV8Engine::unregisterValueIterator(QJSValueIteratorPrivate *data) +{ + m_valueIterators.remove(data); +} + +inline void QV8Engine::invalidateAllIterators() +{ + ValueIteratorList::iterator it; + for (it = m_valueIterators.begin(); it != m_valueIterators.end(); it = it.erase()) + (*it)->invalidate(); + Q_ASSERT(m_valueIterators.isEmpty()); +} + +/*! + \internal + \note property can be index (v8::Integer) or a property (v8::String) name, according to ECMA script + property would be converted to a string. +*/ +inline QJSValuePrivate::PropertyFlags QV8Engine::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) +{ + QJSValuePrivate::PropertyFlags flags = m_originalGlobalObject.getPropertyFlags(object, property); + return flags; +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(const QString& program, const QString& fileName, int lineNumber) +{ + v8::TryCatch tryCatch; + v8::ScriptOrigin scriptOrigin(QJSConverter::toString(fileName), v8::Integer::New(lineNumber - 1)); + v8::Handle<v8::Script> script; + script = v8::Script::Compile(QJSConverter::toString(program), &scriptOrigin); + if (script.IsEmpty()) { + // TODO: Why don't we get the exception, as with Script::Compile()? + // Q_ASSERT(tryCatch.HasCaught()); + v8::Handle<v8::Value> error = v8::Exception::SyntaxError(v8::String::New("")); + setException(error); + return new QJSValuePrivate(this, error); + } + return evaluate(script, tryCatch); +} + +QT_END_NAMESPACE + +#endif // QV8ENGINE_IMPL_P_H diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h new file mode 100644 index 0000000000..22a8d7599f --- /dev/null +++ b/src/qml/qml/v8/qv8engine_p.h @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $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 <private/qv8_p.h> +#include <qjsengine.h> +#include <qjsvalue.h> +#include "qjsvalue_p.h" +#include "qjsvalueiterator_p.h" +#include "qscriptoriginalglobalobject_p.h" +#include "qscripttools_p.h" + +#include <private/qqmlpropertycache_p.h> + +#include "qv8contextwrapper_p.h" +#include "qv8qobjectwrapper_p.h" +#include "qv8stringwrapper_p.h" +#include "qv8typewrapper_p.h" +#include "qv8listwrapper_p.h" +#include "qv8variantwrapper_p.h" +#include "qv8valuetypewrapper_p.h" +#include "qv8sequencewrapper_p.h" + +QT_BEGIN_NAMESPACE + + +// Uncomment the following line to enable global handle debugging. When enabled, all the persistent +// handles allocated using qPersistentNew() (or registered with qPersistentRegsiter()) and disposed +// with qPersistentDispose() are tracked. If you try and do something illegal, like double disposing +// a handle, qFatal() is called. +// #define QML_GLOBAL_HANDLE_DEBUGGING + +#define V8_RESOURCE_TYPE(resourcetype) \ +public: \ + enum { V8ResourceType = QV8ObjectResource:: resourcetype }; \ + virtual QV8ObjectResource::ResourceType resourceType() const { return QV8ObjectResource:: resourcetype; } \ +private: + +#define V8ENGINE() ((QV8Engine *)v8::External::Unwrap(args.Data())) +#define V8FUNCTION(function, engine) v8::FunctionTemplate::New(function, v8::External::Wrap((QV8Engine*)engine))->GetFunction() +#define V8THROW_ERROR(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8THROW_TYPE(string) { \ + v8::ThrowException(v8::Exception::TypeError(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8ENGINE_ACCESSOR() ((QV8Engine *)v8::External::Unwrap(info.Data())); +#define V8THROW_ERROR_SETTER(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return; \ +} + +#define V8_DEFINE_EXTENSION(dataclass, datafunction) \ + static inline dataclass *datafunction(QV8Engine *engine) \ + { \ + static int extensionId = -1; \ + if (extensionId == -1) { \ + QV8Engine::registrationMutex()->lock(); \ + if (extensionId == -1) \ + extensionId = QV8Engine::registerExtension(); \ + QV8Engine::registrationMutex()->unlock(); \ + } \ + dataclass *rv = (dataclass *)engine->extensionData(extensionId); \ + if (!rv) { \ + rv = new dataclass(engine); \ + engine->setExtensionData(extensionId, rv); \ + } \ + return rv; \ + } \ + + +class QV8Engine; +class QV8ObjectResource : public v8::Object::ExternalResource +{ +public: + QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); } + enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, + ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, + ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, + ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType, + SequenceType, LocaleDataType }; + virtual ResourceType resourceType() const = 0; + + QV8Engine *engine; +}; + +template<class T> +inline T *v8_resource_cast(v8::Handle<v8::Object> object) { + QV8ObjectResource *resource = static_cast<QV8ObjectResource *>(object->GetExternalResource()); + return (resource && (quint32)resource->resourceType() == (quint32)T::V8ResourceType)?static_cast<T *>(resource):0; +} + +template<class T> +inline T *v8_resource_check(v8::Handle<v8::Object> object) { + T *resource = static_cast<T *>(object->GetExternalResource()); + Q_ASSERT(resource && resource->resourceType() == (quint32)T::V8ResourceType); + return resource; +} + +// Used to allow a QObject method take and return raw V8 handles without having to expose +// v8 in the public API. +// Use like this: +// class MyClass : public QObject { +// Q_OBJECT +// ... +// Q_INVOKABLE void myMethod(QQmlV8Function*); +// }; +// 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 QV8Engine; +class QQmlV8Function +{ +public: + int Length() const { return _ac; } + v8::Local<v8::Value> operator[](int idx) { return (*_a)->Get(idx); } + QQmlContextData *context() { return _c; } + v8::Handle<v8::Object> qmlGlobal() { return *_g; } + void returnValue(v8::Handle<v8::Value> rv) { *_r = rv; } + QV8Engine *engine() const { return _e; } +private: + friend class QV8QObjectWrapper; + QQmlV8Function(); + QQmlV8Function(const QQmlV8Function &); + QQmlV8Function &operator=(const QQmlV8Function &); + + QQmlV8Function(int length, v8::Handle<v8::Object> &args, + v8::Handle<v8::Value> &rv, v8::Handle<v8::Object> &global, + QQmlContextData *c, QV8Engine *e) + : _ac(length), _a(&args), _r(&rv), _g(&global), _c(c), _e(e) {} + + int _ac; + v8::Handle<v8::Object> *_a; + v8::Handle<v8::Value> *_r; + v8::Handle<v8::Object> *_g; + QQmlContextData *_c; + QV8Engine *_e; +}; + +class QQmlV8Handle +{ +public: + QQmlV8Handle() : d(0) {} + QQmlV8Handle(const QQmlV8Handle &other) : d(other.d) {} + QQmlV8Handle &operator=(const QQmlV8Handle &other) { d = other.d; return *this; } + + static QQmlV8Handle fromHandle(v8::Handle<v8::Value> h) { + return QQmlV8Handle(*h); + } + v8::Handle<v8::Value> toHandle() const { + return v8::Handle<v8::Value>((v8::Value *)d); + } +private: + QQmlV8Handle(void *d) : d(d) {} + void *d; +}; + +class QObject; +class QQmlEngine; +class QQmlValueType; +class QNetworkAccessManager; +class QQmlContextData; + +class Q_AUTOTEST_EXPORT QV8GCCallback +{ +private: + class ThreadData; +public: + static void garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags); + static void registerGcPrologueCallback(); + + class Q_AUTOTEST_EXPORT Node { + public: + typedef void (*PrologueCallback)(Node *node); + Node(PrologueCallback callback); + ~Node(); + + QIntrusiveListNode node; + PrologueCallback prologueCallback; + }; + + static void addGcCallbackNode(Node *node); +}; + +class Q_QML_EXPORT QV8Engine +{ +public: + static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } + static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } + + QV8Engine(QJSEngine* qq,QJSEngine::ContextOwnership ownership = QJSEngine::CreateNewContext); + virtual ~QV8Engine(); + + // This enum should be in sync with QQmlEngine::ObjectOwnership + enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; + + struct Deletable { + virtual ~Deletable() {} + }; + + class Exception + { + typedef QPair<v8::Persistent<v8::Value>, v8::Persistent<v8::Message> > ValueMessagePair; + + v8::Persistent<v8::Value> m_value; + v8::Persistent<v8::Message> m_message; + QStack<ValueMessagePair> m_stack; + + Q_DISABLE_COPY(Exception) + public: + inline Exception(); + inline ~Exception(); + inline void set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message); + inline void clear(); + inline operator bool() const; + inline operator v8::Handle<v8::Value>() const; + inline int lineNumber() const; + inline QStringList backtrace() const; + + inline void push(); + inline void pop(); + }; + + void initDeclarativeGlobalObject(); + void setEngine(QQmlEngine *engine); + QQmlEngine *engine() { return m_engine; } + v8::Local<v8::Object> global() { return m_context->Global(); } + v8::Handle<v8::Context> context() const { return m_context; } + + inline void registerValue(QJSValuePrivate *data); + inline void unregisterValue(QJSValuePrivate *data); + inline void invalidateAllValues(); + + inline void registerValueIterator(QJSValueIteratorPrivate *data); + inline void unregisterValueIterator(QJSValueIteratorPrivate *data); + inline void invalidateAllIterators(); + + QV8ContextWrapper *contextWrapper() { return &m_contextWrapper; } + QV8QObjectWrapper *qobjectWrapper() { return &m_qobjectWrapper; } + QV8TypeWrapper *typeWrapper() { return &m_typeWrapper; } + QV8ListWrapper *listWrapper() { return &m_listWrapper; } + QV8VariantWrapper *variantWrapper() { return &m_variantWrapper; } + QV8ValueTypeWrapper *valueTypeWrapper() { return &m_valueTypeWrapper; } + QV8SequenceWrapper *sequenceWrapper() { return &m_sequenceWrapper; } + + void *xmlHttpRequestData() { return m_xmlHttpRequestData; } + + Deletable *listModelData() { return m_listModelData; } + void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } + + QQmlContextData *callingContext(); + + v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>); + inline QJSValuePrivate::PropertyFlags getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property); + void freezeObject(v8::Handle<v8::Value>); + + inline QString toString(v8::Handle<v8::Value> string); + inline QString toString(v8::Handle<v8::String> string); + static QString toStringStatic(v8::Handle<v8::Value>); + static QString toStringStatic(v8::Handle<v8::String>); + static inline bool startsWithUpper(v8::Handle<v8::String>); + + QVariant toVariant(v8::Handle<v8::Value>, int typeHint); + v8::Handle<v8::Value> fromVariant(const QVariant &); + inline bool isVariant(v8::Handle<v8::Value>); + + // Compile \a source (from \a fileName at \a lineNumber) in QML mode + v8::Local<v8::Script> qmlModeCompile(const QString &source, + const QString &fileName = QString(), + int lineNumber = 1); + v8::Local<v8::Script> qmlModeCompile(const char *source, int sourceLength = -1, + const QString &fileName = QString(), + int lineNumber = 1); + + // Return the QML global "scope" object for the \a ctxt context and \a scope object. + inline v8::Local<v8::Object> qmlScope(QQmlContextData *ctxt, QObject *scope); + + // Return a JS wrapper for the given QObject \a object + inline v8::Handle<v8::Value> newQObject(QObject *object); + inline v8::Handle<v8::Value> newQObject(QObject *object, const ObjectOwnership ownership); + inline bool isQObject(v8::Handle<v8::Value>); + inline QObject *toQObject(v8::Handle<v8::Value>); + + // Return a JS string for the given QString \a string + inline v8::Local<v8::String> toString(const QString &string); + + // Create a new value type object + inline v8::Handle<v8::Value> newValueType(QObject *, int coreIndex, QQmlValueType *); + inline v8::Handle<v8::Value> newValueType(const QVariant &, QQmlValueType *); + + // Create a new sequence type object + inline v8::Handle<v8::Value> newSequence(int sequenceType, QObject *, int coreIndex, bool *succeeded); + + // Create a new QVariant object. This doesn't examine the type of the variant, but always returns + // a QVariant wrapper + inline v8::Handle<v8::Value> newQVariant(const QVariant &); + + // 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(); + + // Return the list of illegal id names (the names of the properties on the global object) + const QStringHash<bool> &illegalNames() const; + + inline void collectGarbage() { gc(); } + static void gc(); + + void clearExceptions(); + void setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message = v8::Handle<v8::Message>()); + v8::Handle<v8::Value> throwException(v8::Handle<v8::Value> value); + bool hasUncaughtException() const; + int uncaughtExceptionLineNumber() const; + QStringList uncaughtExceptionBacktrace() const; + v8::Handle<v8::Value> uncaughtException() const; + void saveException(); + void restoreException(); + +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + // Used for handle debugging + static void registerHandle(void *); + static void releaseHandle(void *); +#endif + + static QMutex *registrationMutex(); + static int registerExtension(); + + inline Deletable *extensionData(int) const; + void setExtensionData(int, Deletable *); + + inline v8::Handle<v8::Value> makeJSValue(bool value); + inline v8::Local<v8::Value> makeJSValue(int value); + inline v8::Local<v8::Value> makeJSValue(uint value); + inline v8::Local<v8::Value> makeJSValue(double value); + inline v8::Handle<v8::Value> makeJSValue(QJSValue::SpecialValue value); + inline v8::Local<v8::Value> makeJSValue(const QString &value); + + inline QScriptPassPointer<QJSValuePrivate> evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + QScriptPassPointer<QJSValuePrivate> evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch); + + QScriptPassPointer<QJSValuePrivate> newArray(uint length); + v8::Local<v8::Object> newVariant(const QVariant &variant); + + v8::Local<v8::Array> variantListToJS(const QVariantList &lst); + QVariantList variantListFromJS(v8::Handle<v8::Array> jsArray); + + v8::Local<v8::Object> variantMapToJS(const QVariantMap &vmap); + QVariantMap variantMapFromJS(v8::Handle<v8::Object> jsObject); + + v8::Handle<v8::Value> variantToJS(const QVariant &value); + QVariant variantFromJS(v8::Handle<v8::Value> value); + + v8::Handle<v8::Value> metaTypeToJS(int type, const void *data); + bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data); + + bool convertToNativeQObject(v8::Handle<v8::Value> value, + const QByteArray &targetType, + void **result); + + QVariant &variantValue(v8::Handle<v8::Value> value); + + QJSValue scriptValueFromInternal(v8::Handle<v8::Value>) const; + + void emitSignalHandlerException(); + + // 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, int line, int column); + + QObject *qtObjectFromJS(v8::Handle<v8::Value> value); + QSet<int> visitedConversionObjects; + + static QDateTime qtDateTimeFromJsDate(double jsDate); + + void addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle); + void addRelationshipForGC(QObject *object, QObject *other); + + struct ThreadData { + ThreadData(); + ~ThreadData(); + v8::Isolate* isolate; + bool gcPrologueCallbackRegistered; + QIntrusiveList<QV8GCCallback::Node, &QV8GCCallback::Node::node> gcCallbackNodes; + }; + + static bool hasThreadData(); + static ThreadData* threadData(); + static void ensurePerThreadIsolate(); + + v8::Persistent<v8::Object> m_strongReferencer; + +protected: + QJSEngine* q; + QQmlEngine *m_engine; + bool m_ownsV8Context; + v8::Persistent<v8::Context> m_context; + QScriptOriginalGlobalObject m_originalGlobalObject; + + QV8StringWrapper m_stringWrapper; + QV8ContextWrapper m_contextWrapper; + QV8QObjectWrapper m_qobjectWrapper; + QV8TypeWrapper m_typeWrapper; + QV8ListWrapper m_listWrapper; + QV8VariantWrapper m_variantWrapper; + QV8ValueTypeWrapper m_valueTypeWrapper; + QV8SequenceWrapper m_sequenceWrapper; + + v8::Persistent<v8::Function> m_getOwnPropertyNames; + v8::Persistent<v8::Function> m_freezeObject; + + void *m_xmlHttpRequestData; + + QVector<Deletable *> m_extensionData; + Deletable *m_listModelData; + + QStringHash<bool> m_illegalNames; + + Exception m_exception; + + QElapsedTimer m_time; + QHash<QString, qint64> m_startedTimers; + + QHash<QString, quint32> m_consoleCount; + + QVariant toBasicVariant(v8::Handle<v8::Value>); + + void initializeGlobal(v8::Handle<v8::Object>); + + double qtDateTimeToJsDate(const QDateTime &dt); + +private: + static v8::Persistent<v8::Object> *findOwnerAndStrength(QObject *object, bool *shouldBeStrong); + + typedef QScriptIntrusiveList<QJSValuePrivate, &QJSValuePrivate::m_node> ValueList; + ValueList m_values; + typedef QScriptIntrusiveList<QJSValueIteratorPrivate, &QJSValueIteratorPrivate::m_node> ValueIteratorList; + ValueIteratorList m_valueIterators; + + Q_DISABLE_COPY(QV8Engine) +}; + +// Allocate a new Persistent handle. *ALL* persistent handles in QML must be allocated +// using this method. +template<class T> +v8::Persistent<T> qPersistentNew(v8::Handle<T> that) +{ + v8::Persistent<T> rv = v8::Persistent<T>::New(that); +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*rv); +#endif + return rv; +} + +// Register a Persistent handle that was returned to you by V8 (such as by +// v8::Context::New). This allows us to do handle tracking on these handles too. +template<class T> +void qPersistentRegister(v8::Persistent<T> handle) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*handle); +#else + Q_UNUSED(handle); +#endif +} + +// Dispose and clear a persistent handle. *ALL* persistent handles in QML must be +// disposed using this method. +template<class T> +void qPersistentDispose(v8::Persistent<T> &that) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::releaseHandle(*that); +#endif + that.Dispose(); + that.Clear(); +} + +QString QV8Engine::toString(v8::Handle<v8::Value> string) +{ + return m_stringWrapper.toString(string->ToString()); +} + +QString QV8Engine::toString(v8::Handle<v8::String> string) +{ + return m_stringWrapper.toString(string); +} + +bool QV8Engine::isVariant(v8::Handle<v8::Value> value) +{ + return m_variantWrapper.isVariant(value); +} + +v8::Local<v8::Object> QV8Engine::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + return m_contextWrapper.qmlScope(ctxt, scope); +} + +bool QV8Engine::isQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.isQObject(v8::Handle<v8::Object>::Cast(obj)):false; +} + +QObject *QV8Engine::toQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.toQObject(v8::Handle<v8::Object>::Cast(obj)):0; +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object) +{ + return m_qobjectWrapper.newQObject(object); +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object, const ObjectOwnership ownership) +{ + if (!object) + return v8::Null(); + + v8::Handle<v8::Value> result = newQObject(object); + QQmlData *ddata = QQmlData::get(object, true); + if (ownership == JavaScriptOwnership && ddata) { + ddata->indestructible = false; + ddata->explicitIndestructibleSet = true; + } + return result; +} + +v8::Local<v8::String> QV8Engine::toString(const QString &string) +{ + return m_stringWrapper.toString(string); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(QObject *object, int property, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(object, property, type); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(const QVariant &value, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(value, type); +} + +v8::Handle<v8::Value> QV8Engine::newSequence(int sequenceType, QObject *object, int property, bool *succeeded) +{ + return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded); +} + +// XXX Can this be made more optimal? It is called prior to resolving each and every +// unqualified name in QV8ContextWrapper. +bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string) +{ + uint16_t c = string->GetCharacter(0); + return (c >= 'A' && c <= 'Z') || + (c > 127 && QChar::category(c) == QChar::Letter_Uppercase); +} + +QV8Engine::Deletable *QV8Engine::extensionData(int index) const +{ + if (index < m_extensionData.count()) + return m_extensionData[index]; + else + return 0; +} + +QT_END_NAMESPACE + +#endif // QQMLV8ENGINE_P_H diff --git a/src/qml/qml/v8/qv8include.cpp b/src/qml/qml/v8/qv8include.cpp new file mode 100644 index 0000000000..89f60f256e --- /dev/null +++ b/src/qml/qml/v8/qv8include.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8include_p.h" + +#include <QtQml/qjsengine.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qfile.h> + +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QV8Include::QV8Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Function> callback) +: m_engine(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) +{ + m_qmlglobal = qPersistentNew<v8::Object>(qmlglobal); + if (!callback.IsEmpty()) + m_callbackFunction = qPersistentNew<v8::Function>(callback); + + m_resultObject = qPersistentNew<v8::Object>(resultValue()); + + m_network = engine->networkAccessManager(); + + QNetworkRequest request; + request.setUrl(url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +} + +QV8Include::~QV8Include() +{ + delete m_reply; m_reply = 0; + qPersistentDispose(m_callbackFunction); + qPersistentDispose(m_resultObject); +} + +v8::Local<v8::Object> QV8Include::resultValue(Status status) +{ + // XXX It seems inefficient to create this object from scratch each time. + v8::Local<v8::Object> result = v8::Object::New(); + result->Set(v8::String::New("OK"), v8::Integer::New(Ok)); + result->Set(v8::String::New("LOADING"), v8::Integer::New(Loading)); + result->Set(v8::String::New("NETWORK_ERROR"), v8::Integer::New(NetworkError)); + result->Set(v8::String::New("EXCEPTION"), v8::Integer::New(Exception)); + + result->Set(v8::String::New("status"), v8::Integer::New(status)); + + return result; +} + +void QV8Include::callback(QV8Engine *engine, v8::Handle<v8::Function> callback, v8::Handle<v8::Object> status) +{ + if (!callback.IsEmpty()) { + v8::Handle<v8::Value> args[] = { status }; + v8::TryCatch tc; + callback->Call(engine->global(), 1, args); + } +} + +v8::Handle<v8::Object> QV8Include::result() +{ + return m_resultObject; +} + +#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 +void QV8Include::finished() +{ + m_redirectCount++; + + if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + m_url = m_url.resolved(redirect.toUrl()); + delete m_reply; + + QNetworkRequest request; + request.setUrl(m_url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + return; + } + } + + v8::HandleScope handle_scope; + + if (m_reply->error() == QNetworkReply::NoError) { + QByteArray data = m_reply->readAll(); + + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QQmlContextData *importContext = new QQmlContextData; + importContext->isInternal = true; + importContext->isJSContext = true; + importContext->url = m_url; + importContext->isPragmaLibraryContext = m_context->isPragmaLibraryContext; + importContext->setParent(m_context, true); + + v8::Context::Scope ctxtscope(m_engine->context()); + v8::TryCatch try_catch; + + v8::Local<v8::Script> script = m_engine->qmlModeCompile(code, m_url.toString()); + + if (!try_catch.HasCaught()) { + m_engine->contextWrapper()->addSubContext(m_qmlglobal, script, importContext); + script->Run(m_qmlglobal); + } + + if (try_catch.HasCaught()) { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Exception)); + m_resultObject->Set(v8::String::New("exception"), try_catch.Exception()); + } else { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Ok)); + } + } else { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(NetworkError)); + } + + callback(m_engine, m_callbackFunction, m_resultObject); + + disconnect(); + deleteLater(); +} + +/* + Documented in qv8engine.cpp +*/ +v8::Handle<v8::Value> QV8Include::include(const v8::Arguments &args) +{ + if (args.Length() == 0) + return v8::Undefined(); + + QV8Engine *engine = V8ENGINE(); + QQmlContextData *context = engine->callingContext(); + + if (!context || !context->isJSContext) + V8THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); + + QUrl url(context->resolvedUrl(QUrl(engine->toString(args[0]->ToString())))); + + v8::Local<v8::Function> callbackFunction; + if (args.Length() >= 2 && args[1]->IsFunction()) + callbackFunction = v8::Local<v8::Function>::Cast(args[1]); + + QString localFile = QQmlEnginePrivate::urlToLocalFileOrQrc(url); + + v8::Local<v8::Object> result; + + if (localFile.isEmpty()) { + + QV8Include *i = new QV8Include(url, engine, context, + v8::Context::GetCallingQmlGlobal(), + callbackFunction); + result = v8::Local<v8::Object>::New(i->result()); + + } else { + + QFile f(localFile); + + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QQmlContextData *importContext = new QQmlContextData; + importContext->isInternal = true; + importContext->isJSContext = true; + importContext->url = url; + importContext->setParent(context, true); + + v8::TryCatch try_catch; + + v8::Local<v8::Script> script = engine->qmlModeCompile(code, url.toString()); + + if (!try_catch.HasCaught()) { + v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal(); + engine->contextWrapper()->addSubContext(qmlglobal, script, importContext); + script->Run(qmlglobal); + } + + if (try_catch.HasCaught()) { + result = resultValue(Exception); + result->Set(v8::String::New("exception"), try_catch.Exception()); + } else { + result = resultValue(Ok); + } + + } else { + result = resultValue(NetworkError); + } + + callback(engine, callbackFunction, result); + } + + if (result.IsEmpty()) + return v8::Undefined(); + else + return result; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8include_p.h b/src/qml/qml/v8/qv8include_p.h new file mode 100644 index 0000000000..f1e57b3eee --- /dev/null +++ b/src/qml/qml/v8/qv8include_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8INCLUDE_P_H +#define QV8INCLUDE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> + +#include <private/qqmlcontext_p.h> +#include <private/qqmlguard_p.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class QNetworkAccessManager; +class QNetworkReply; +class QV8Engine; +class QV8Include : public QObject +{ + Q_OBJECT +public: + enum Status { + Ok = 0, + Loading = 1, + NetworkError = 2, + Exception = 3 + }; + + static v8::Handle<v8::Value> include(const v8::Arguments &args); + +private slots: + void finished(); + +private: + QV8Include(const QUrl &, QV8Engine *, QQmlContextData *, + v8::Handle<v8::Object>, v8::Handle<v8::Function>); + ~QV8Include(); + + v8::Handle<v8::Object> result(); + + static v8::Local<v8::Object> resultValue(Status status = Loading); + static void callback(QV8Engine *engine, v8::Handle<v8::Function> callback, v8::Handle<v8::Object> status); + + QV8Engine *m_engine; + QNetworkAccessManager *m_network; + QQmlGuard<QNetworkReply> m_reply; + + QUrl m_url; + int m_redirectCount; + + v8::Persistent<v8::Function> m_callbackFunction; + v8::Persistent<v8::Object> m_resultObject; + + QQmlGuardedContextData m_context; + v8::Persistent<v8::Object> m_qmlglobal; +}; + +QT_END_NAMESPACE + +#endif // QV8INCLUDE_P_H + diff --git a/src/qml/qml/v8/qv8listwrapper.cpp b/src/qml/qml/v8/qv8listwrapper.cpp new file mode 100644 index 0000000000..d6eab7af34 --- /dev/null +++ b/src/qml/qml/v8/qv8listwrapper.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8listwrapper_p.h" +#include "qv8engine_p.h" +#include <private/qqmllist_p.h> + +QT_BEGIN_NAMESPACE + +class QV8ListResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ListType); +public: + QV8ListResource(QV8Engine *engine) : QV8ObjectResource(engine) {} + + QQmlGuard<QObject> object; + QQmlListProperty<QObject> property; + int propertyType; +}; + +QV8ListWrapper::QV8ListWrapper() +: m_engine(0) +{ +} + +QV8ListWrapper::~QV8ListWrapper() +{ +} + +void QV8ListWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, 0, 0, Enumerator); + ft->InstanceTemplate()->SetIndexedPropertyHandler(IndexedGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("length"), LengthGetter, 0, + v8::Handle<v8::Value>(), v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} + +void QV8ListWrapper::destroy() +{ + qPersistentDispose(m_constructor); +} + +v8::Handle<v8::Value> QV8ListWrapper::newList(QObject *object, int propId, int propType) +{ + if (!object || propId == -1) + return v8::Null(); + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ListResource *r = new QV8ListResource(m_engine); + r->object = object; + r->propertyType = propType; + void *args[] = { &r->property, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args); + rv->SetExternalResource(r); + return rv; +} + +v8::Handle<v8::Value> QV8ListWrapper::newList(const QQmlListProperty<QObject> &prop, int propType) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ListResource *r = new QV8ListResource(m_engine); + r->object = prop.object; + r->property = prop; + r->propertyType = propType; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8ListWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(obj); + if (resource) return toVariant(resource); + else return QVariant(); +} + +QVariant QV8ListWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ListType); + QV8ListResource *resource = static_cast<QV8ListResource *>(r); + + if (!resource->object) + return QVariant(); + + return QVariant::fromValue(QQmlListReferencePrivate::init(resource->property, resource->propertyType, + m_engine->engine())); +} + +v8::Handle<v8::Value> QV8ListWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8ListWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return value; +} + +v8::Handle<v8::Value> QV8ListWrapper::IndexedGetter(uint32_t index, const v8::AccessorInfo &info) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Undefined(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + if (index < count && resource->property.at) { + return resource->engine->newQObject(resource->property.at(&resource->property, index)); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8ListWrapper::LengthGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Undefined(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + + return v8::Integer::NewFromUnsigned(count); +} + +v8::Handle<v8::Array> QV8ListWrapper::Enumerator(const v8::AccessorInfo &info) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Array::New(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + + v8::Local<v8::Array> rv = v8::Array::New(count); + + for (uint ii = 0; ii < count; ++ii) + rv->Set(ii, v8::Number::New(ii)); + + return rv; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8listwrapper_p.h b/src/qml/qml/v8/qv8listwrapper_p.h new file mode 100644 index 0000000000..1e4bab06d5 --- /dev/null +++ b/src/qml/qml/v8/qv8listwrapper_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8LISTWRAPPER_P_H +#define QV8LISTWRAPPER_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 <QtQml/qqmllist.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8ListWrapper +{ +public: + QV8ListWrapper(); + ~QV8ListWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Handle<v8::Value> newList(QObject *, int, int); + v8::Handle<v8::Value> newList(const QQmlListProperty<QObject> &, int); + QVariant toVariant(v8::Handle<v8::Object>); + QVariant toVariant(QV8ObjectResource *); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> IndexedGetter(uint32_t index, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> LengthGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8LISTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8profiler_p.h b/src/qml/qml/v8/qv8profiler_p.h new file mode 100644 index 0000000000..45df5a17c4 --- /dev/null +++ b/src/qml/qml/v8/qv8profiler_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8-profiler.h> diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp new file mode 100644 index 0000000000..b84ae339be --- /dev/null +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -0,0 +1,2113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8qobjectwrapper_p.h" +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlguard_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qjsvalue_p.h> +#include <private/qscript_impl_p.h> +#include <private/qqmlaccessors_p.h> +#include <private/qqmlexpression_p.h> + +#include <QtQml/qjsvalue.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +Q_DECLARE_METATYPE(QJSValue); +Q_DECLARE_METATYPE(QQmlV8Handle); + +QT_BEGIN_NAMESPACE + +#if defined(__GNUC__) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +// The code in this file does not violate strict aliasing, but GCC thinks it does +// so turn off the warnings for us to have a clean build +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# endif +#endif + +#define QOBJECT_TOSTRING_INDEX -2 +#define QOBJECT_DESTROY_INDEX -3 + +// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work +// correctly in a worker thread + +class QV8QObjectResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(QObjectType); + +public: + QV8QObjectResource(QV8Engine *engine, QObject *object); + + QQmlGuard<QObject> object; +}; + +class QV8QObjectInstance : public QQmlGuard<QObject> +{ +public: + QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w) + : QQmlGuard<QObject>(o), wrapper(w) + { + } + + ~QV8QObjectInstance() + { + qPersistentDispose(v8object); + } + + virtual void objectDestroyed(QObject *o) + { + if (wrapper) + wrapper->m_taintedObjects.remove(o); + delete this; + } + + v8::Persistent<v8::Object> v8object; + QV8QObjectWrapper *wrapper; +}; + +class QV8SignalHandlerResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(SignalHandlerType) +public: + QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index); + + QQmlGuard<QObject> object; + int index; +}; + +namespace { + +template<typename A, typename B, typename C, typename D, typename E> +class MaxSizeOf5 { + template<typename Z, typename X> + struct SMax { + static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X); + }; +public: + static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size; +}; + +struct CallArgument { + inline CallArgument(); + inline ~CallArgument(); + inline void *dataPtr(); + + inline void initAsType(int type); + inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>); + inline v8::Handle<v8::Value> toValue(QV8Engine *); + +private: + CallArgument(const CallArgument &); + + inline void cleanup(); + + union { + float floatValue; + double doubleValue; + quint32 intValue; + bool boolValue; + QObject *qobjectPtr; + + char allocData[MaxSizeOf5<QVariant, + QString, + QList<QObject *>, + QJSValue, + QQmlV8Handle>::Size]; + qint64 q_for_alignment; + }; + + // Pointers to allocData + union { + QString *qstringPtr; + QVariant *qvariantPtr; + QList<QObject *> *qlistPtr; + QJSValue *qjsValuePtr; + QQmlV8Handle *handlePtr; + }; + + int type; +}; +} + +QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) +: QV8ObjectResource(engine), object(object) +{ +} + +QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index) +: QV8ObjectResource(engine), object(object), index(index) +{ +} + +static QAtomicInt objectIdCounter(1); + +QV8QObjectWrapper::QV8QObjectWrapper() +: m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1)) +{ +} + +QV8QObjectWrapper::~QV8QObjectWrapper() +{ + for (TaintedHash::Iterator iter = m_taintedObjects.begin(); + iter != m_taintedObjects.end(); + ++iter) { + (*iter)->wrapper = 0; + } + m_taintedObjects.clear(); +} + +void QV8QObjectWrapper::destroy() +{ + qDeleteAll(m_connections); + m_connections.clear(); + + qPersistentDispose(m_hiddenObject); + qPersistentDispose(m_destroySymbol); + qPersistentDispose(m_toStringSymbol); + qPersistentDispose(m_signalHandlerConstructor); + qPersistentDispose(m_methodConstructor); + qPersistentDispose(m_constructor); +} + +struct ReadAccessor { + static inline void Indirect(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Direct(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Accessor(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(property.accessors); + + property.accessors->read(object, property.accessorData, output); + if (n) property.accessors->notifier(object, property.accessorData, n); + } +}; + +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v) +{ return v8::Integer::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v) +{ return v8::Integer::NewFromUnsigned(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v) +{ return v8::Boolean::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v) +{ return e->toString(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v) +{ return v8::Number::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v) +{ return v8::Number::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v) +{ return e->newQObject(v); } + +template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &, + void *, QQmlNotifier **)> +static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + v8::Handle<v8::Object> This = info.This(); + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); + + QObject *object = resource->object; + if (!object) return v8::Undefined(); + + QQmlPropertyData *property = + (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + + QQmlEngine *engine = resource->engine->engine(); + QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; + + T value = T(); + + if (ep && ep->propertyCapture) { + if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) { + QQmlNotifier *notifier = 0; + ReadFunction(object, *property, &value, ¬ifier); + if (notifier) ep->captureProperty(notifier); + } else if (!property->isConstant()) { + ep->captureProperty(object, property->coreIndex, property->notifyIndex); + ReadFunction(object, *property, &value, 0); + } else { + ReadFunction(object, *property, &value, 0); + } + } else { + ReadFunction(object, *property, &value, 0); + } + + return valueToHandle(resource->engine, value); +} + +#define FAST_GETTER_FUNCTION(property, cpptype) \ + (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>))) + +static quint32 toStringHash = -1; +static quint32 destroyHash = -1; + +void QV8QObjectWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + + m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString")); + m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy")); + m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New()); + + m_toStringString = QHashedV8String(m_toStringSymbol); + m_destroyString = QHashedV8String(m_destroySymbol); + + toStringHash = m_toStringString.hash(); + destroyHash = m_destroyString.hash(); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions +#define CREATE_FUNCTION_SOURCE \ + "(function(method) { "\ + "return (function(object, data, qmlglobal) { "\ + "return (function() { "\ + "return method(object, data, qmlglobal, arguments.length, arguments); "\ + "});"\ + "});"\ + "})" + v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0, + v8::Handle<v8::String>(), v8::Script::NativeMode); +#undef CREATE_FUNCTION_SOURCE + v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run()); + v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction(); + v8::Handle<v8::Value> args[] = { invokeFn }; + v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args)); + m_methodConstructor = qPersistentNew<v8::Function>(createFn); + } + + v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine); + v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum); + ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum); + m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + + { + v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject(); + prototype->Set(v8::String::New("connect"), connect, v8::DontEnum); + prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum); + } +} + +bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj) +{ + return v8_resource_cast<QV8QObjectResource>(obj) != 0; +} + +QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj) +{ + QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj); + return r?r->object:0; +} + +// r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType) +QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType); + return static_cast<QV8QObjectResource *>(r)->object; +} + +// Load value properties +template<void (*ReadFunction)(QObject *, const QQmlPropertyData &, + void *, QQmlNotifier **)> +static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, + const QQmlPropertyData &property, + QQmlNotifier **notifier) +{ + Q_ASSERT(!property.isFunction()); + + if (property.isQObject()) { + QObject *rv = 0; + ReadFunction(object, property, &rv, notifier); + return engine->newQObject(rv); + } else if (property.isQList()) { + return engine->listWrapper()->newList(object, property.coreIndex, property.propType); + } else if (property.propType == QMetaType::QReal) { + qreal v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Int || property.isEnum()) { + int v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Bool) { + bool v = false; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::QString) { + QString v; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::UInt) { + uint v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Float) { + float v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Double) { + double v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.isV8Handle()) { + QQmlV8Handle handle; + ReadFunction(object, property, &handle, notifier); + return handle.toHandle(); + } else if (property.isQVariant()) { + QVariant v; + ReadFunction(object, property, &v, notifier); + return engine->fromVariant(v); + } else if (QQmlValueTypeFactory::isValueType((uint)property.propType) + && engine->engine()) { + Q_ASSERT(notifier == 0); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + QQmlValueType *valueType = ep->valueTypes[property.propType]; + if (valueType) + return engine->newValueType(object, property.coreIndex, valueType); + } else { + Q_ASSERT(notifier == 0); + + // see if it's a sequence type + bool succeeded = false; + v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex, + &succeeded); + if (succeeded) + return retn; + } + + if (property.propType == QVariant::Invalid) { + QMetaProperty p = object->metaObject()->property(property.coreIndex); + qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " + "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); + return v8::Undefined(); + } else { + QVariant v(property.propType, (void *)0); + ReadFunction(object, property, v.data(), notifier); + return engine->fromVariant(v); + } +} + +v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + const QHashedV8String &property, + QV8QObjectWrapper::RevisionMode revisionMode) +{ + // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these + // will be a faster way of creating QObject method objects. + struct MethodClosure { + static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + int index) { + v8::Handle<v8::Value> argv[] = { + objectHandle?*objectHandle:engine->newQObject(object), + v8::Integer::New(index) + }; + return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv); + } + static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + int index) { + v8::Handle<v8::Value> argv[] = { + objectHandle?*objectHandle:engine->newQObject(object), + v8::Integer::New(index), + v8::Context::GetCallingQmlGlobal() + }; + return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv); + } + }; + + { + // Comparing the hash first actually makes a measurable difference here, at least on x86 + quint32 hash = property.hash(); + if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX); + } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX); + } + } + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + QQmlData *ddata = QQmlData::get(object, false); + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(property); + else + result = QQmlPropertyCache::property(engine->engine(), object, property, local); + } + + if (!result) + return v8::Handle<v8::Value>(); + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return v8::Handle<v8::Value>(); + } + + if (result->isFunction()) { + if (result->isVMEFunction()) { + return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex); + } else if (result->isV8Function()) { + return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex); + } else if (result->isSignalHandler()) { + v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance(); + QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex); + handler->SetExternalResource(r); + return handler; + } else { + return MethodClosure::create(engine, object, objectHandle, result->coreIndex); + } + } + + QQmlEnginePrivate *ep = + engine->engine()?QQmlEnginePrivate::get(engine->engine()):0; + + if (result->hasAccessors()) { + QQmlNotifier *n = 0; + QQmlNotifier **nptr = 0; + + if (ep && ep->propertyCapture && result->accessors->notifier) + nptr = &n; + + v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr); + + if (result->accessors->notifier) { + if (n) ep->captureProperty(n); + } else { + ep->captureProperty(object, result->coreIndex, result->notifyIndex); + } + + return rv; + } + + if (ep && !result->isConstant()) { + + if (result->coreIndex == 0) + ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier()); + else + ep->captureProperty(object, result->coreIndex, result->notifyIndex); + } + + if (result->isVMEProperty()) { + typedef QQmlVMEMetaObject VMEMO; + VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject())); + return vmemo->vmeProperty(result->coreIndex); + } else if (result->isDirect()) { + return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0); + } else { + return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0); + } +} + +// Setter for writable properties. Shared between the interceptor and fast property accessor +static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property, + v8::Handle<v8::Value> value) +{ + QQmlBinding *newBinding = 0; + + if (value->IsFunction()) { + QQmlContextData *context = engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columNumber = frame->GetColumn(); + QString url = engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, object, context); + newBinding->setSourceLocation(url, lineNumber, columNumber); + newBinding->setTarget(object, *property, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding); + if (oldBinding) + oldBinding->destroy(); + +#define PROPERTY_STORE(cpptype, value) \ + cpptype o = value; \ + int status = -1; \ + int flags = 0; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv); + + + if (value->IsNull() && property->isQObject()) { + PROPERTY_STORE(QObject*, 0); + } else if (value->IsUndefined() && property->isResettable()) { + void *a[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a); + } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) { + PROPERTY_STORE(QVariant, QVariant()); + } else if (value->IsUndefined()) { + QString error = QLatin1String("Cannot assign [undefined] to ") + + QLatin1String(QMetaType::typeName(property->propType)); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + } else if (value->IsFunction()) { + // this is handled by the binding creation above + } else if (property->propType == QMetaType::Int && value->IsNumber()) { + PROPERTY_STORE(int, qRound(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::QReal && value->IsNumber()) { + PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::Float && value->IsNumber()) { + PROPERTY_STORE(float, float(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::Double && value->IsNumber()) { + PROPERTY_STORE(double, double(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::QString && value->IsString()) { + PROPERTY_STORE(QString, engine->toString(value->ToString())); + } else if (property->isVMEProperty()) { + static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value); + } else { + QVariant v; + if (property->isQList()) + v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + else + v = engine->toVariant(value, property->propType); + + QQmlContextData *context = engine->callingContext(); + if (!QQmlPropertyPrivate::write(object, *property, v, context)) { + const char *valueType = 0; + if (v.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(v.userType()); + + QString error = QLatin1String("Cannot assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(QMetaType::typeName(property->propType)); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + } + } +} + +bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property, + v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode) +{ + if (engine->qobjectWrapper()->m_toStringString == property || + engine->qobjectWrapper()->m_destroyString == property) + return true; + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + result = QQmlPropertyCache::property(engine->engine(), object, property, local); + + if (!result) + return false; + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return false; + } + + if (!result->isWritable() && !result->isQList()) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + engine->toString(property.string()) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return true; + } + + StoreProperty(engine, object, result, value); + + return true; +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Handle<v8::Value>(); + + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QV8Engine *v8engine = resource->engine; + v8::Handle<v8::Value> This = info.This(); + v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) + return result; + + if (QV8Engine::startsWithUpper(property)) { + // Check for attached properties + QQmlContextData *context = v8engine->callingContext(); + + if (context && context->imports) { + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (r.scriptIndex != -1) { + return v8::Undefined(); + } else if (r.type) { + return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums); + } else if (r.importNamespace) { + return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace, + QV8TypeWrapper::ExcludeEnums); + } + Q_ASSERT(!"Unreachable"); + } + } + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return value; + + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QV8Engine *v8engine = resource->engine; + bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision); + + if (!result) { + QString error = QLatin1String("Cannot assign to non-existent property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); + return value; + } + + return value; +} + +v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Handle<v8::Integer>(); + + QV8Engine *engine = resource->engine; + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local); + + if (!result) + return v8::Handle<v8::Integer>(); + else if (!result->isWritable() && !result->isQList()) + return v8::Integer::New(v8::ReadOnly | v8::DontDelete); + else + return v8::Integer::New(v8::DontDelete); +} + +v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Array::New(); + + QObject *object = resource->object; + + QStringList result; + + QQmlEnginePrivate *ep = resource->engine->engine() + ? QQmlEnginePrivate::get(resource->engine->engine()) + : 0; + + QQmlPropertyCache *cache = 0; + QQmlData *ddata = QQmlData::get(object); + if (ddata) + cache = ddata->propertyCache; + + if (!cache) { + cache = ep ? ep->cache(object) : 0; + if (cache) { + if (ddata) { cache->addref(); ddata->propertyCache = cache; } + } else { + // Not cachable - fall back to QMetaObject (eg. dynamic meta object) + const QMetaObject *mo = object->metaObject(); + int pc = mo->propertyCount(); + int po = mo->propertyOffset(); + for (int i=po; i<pc; ++i) + result << QString::fromUtf8(mo->property(i).name()); + } + } else { + result = cache->propertyNames(); + } + + v8::Local<v8::Array> rv = v8::Array::New(result.count()); + + for (int ii = 0; ii < result.count(); ++ii) + rv->Set(ii, resource->engine->toString(result.at(ii))); + + return rv; +} + +static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return; + + QObject *object = resource->object; + + QQmlPropertyData *property = + (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + + int index = property->coreIndex; + + QQmlData *ddata = QQmlData::get(object, false); + Q_ASSERT(ddata); + Q_ASSERT(ddata->propertyCache); + + QQmlPropertyData *pdata = ddata->propertyCache->property(index); + Q_ASSERT(pdata); + + Q_ASSERT(pdata->isWritable() || pdata->isQList()); + + StoreProperty(resource->engine, object, pdata, value); +} + +static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>, + const v8::AccessorInfo& info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return; + + QV8Engine *v8engine = resource->engine; + + QString error = QLatin1String("Cannot assign to read-only property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); +} + +static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *) +{ + Q_ASSERT(handle->IsObject()); + + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject()); + + Q_ASSERT(resource); + + QObject *object = resource->object; + if (object) { + QQmlData *ddata = QQmlData::get(object, false); + if (ddata) { + ddata->v8object.Clear(); + if (!object->parent() && !ddata->indestructible) + object->deleteLater(); + } + } + + qPersistentDispose(handle); +} + +static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data) +{ + QV8QObjectInstance *instance = (QV8QObjectInstance *)data; + instance->v8object.Clear(); + qPersistentDispose(handle); +} + +v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine) +{ + Q_ASSERT(object); + Q_ASSERT(this->engine); + + Q_ASSERT(QQmlData::get(object, false)); + Q_ASSERT(QQmlData::get(object, false)->propertyCache == this); + + // Setup constructor + if (constructor.IsEmpty()) { + v8::Local<v8::FunctionTemplate> ft; + + QString toString = QLatin1String("toString"); + QString destroy = QLatin1String("destroy"); + + // As we use hash linking, it is possible that iterating over the values can give duplicates. + // To combat this, we must unique'ify our properties. + StringCache uniqueHash; + if (stringCache.isLinked()) + uniqueHash.reserve(stringCache.count()); + + // XXX TODO: Enables fast property accessors. These more than double the property access + // performance, but the cost of setting up this structure hasn't been measured so + // its not guarenteed that this is a win overall. We need to try and measure the cost. + for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) { + if (stringCache.isLinked()) { + if (uniqueHash.contains(iter)) + continue; + uniqueHash.insert(iter); + } + + QQmlPropertyData *property = *iter; + if (property->notFullyResolved()) resolve(property); + + if (property->isFunction()) + continue; + + v8::AccessorGetter fastgetter = 0; + v8::AccessorSetter fastsetter = FastValueSetter; + if (!property->isWritable()) + fastsetter = FastValueSetterReadOnly; + + if (property->isQObject()) + fastgetter = FAST_GETTER_FUNCTION(property, QObject*); + else if (property->propType == QMetaType::Int || property->isEnum()) + fastgetter = FAST_GETTER_FUNCTION(property, int); + else if (property->propType == QMetaType::Bool) + fastgetter = FAST_GETTER_FUNCTION(property, bool); + else if (property->propType == QMetaType::QString) + fastgetter = FAST_GETTER_FUNCTION(property, QString); + else if (property->propType == QMetaType::UInt) + fastgetter = FAST_GETTER_FUNCTION(property, uint); + else if (property->propType == QMetaType::Float) + fastgetter = FAST_GETTER_FUNCTION(property, float); + else if (property->propType == QMetaType::Double) + fastgetter = FAST_GETTER_FUNCTION(property, double); + + if (fastgetter) { + QString name = iter.key(); + if (name == toString || name == destroy) + continue; + + if (ft.IsEmpty()) { + ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, + QV8QObjectWrapper::Setter, + QV8QObjectWrapper::Query, + 0, + QV8QObjectWrapper::Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + } + + // We wrap the raw QQmlPropertyData pointer here. This is safe as the + // pointer will remain valid at least as long as the lifetime of any QObject's of + // this type and the property accessor checks if the object is 0 (deleted) before + // dereferencing the pointer. + ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter, + v8::External::Wrap(property)); + } + } + + if (ft.IsEmpty()) { + constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor); + } else { + ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, + QV8QObjectWrapper::Setter, + QV8QObjectWrapper::Query, + 0, + QV8QObjectWrapper::Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + + QQmlCleanup::addToEngine(this->engine); + } + + v8::Local<v8::Object> result = constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + result->SetExternalResource(r); + return result; +} + +v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine) +{ + v8::Local<v8::Object> rv; + + if (!ddata->propertyCache && engine->engine()) { + ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object); + if (ddata->propertyCache) ddata->propertyCache->addref(); + } + + if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) { + rv = ddata->propertyCache->newQObject(object, engine); + } else { + // XXX NewInstance() should be optimized + rv = m_constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + rv->SetExternalResource(r); + } + + return rv; +} + +/* +As V8 doesn't support an equality callback, for QObject's we have to return exactly the same +V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two +pronged strategy: + 1. If there is no current outstanding V8 handle to the QObject, we create one and store a + persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that + "owns" this handle by setting the QQmlData::v8objectid to the id of this + QV8QObjectWrapper. + 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create + an entry in the m_taintedObject hash where we store the handle and mark the object as + "tainted" in the QQmlData::hasTaintedV8Object flag. +We have to mark the object as tainted to ensure that we search our m_taintedObject hash even +in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has +released the handle. +*/ +v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) +{ + if (!object) + return v8::Null(); + + if (QObjectPrivate::get(object)->wasDeleted) + return v8::Undefined(); + + QQmlData *ddata = QQmlData::get(object, true); + + if (!ddata) + return v8::Undefined(); + + if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) { + // We own the v8object + return v8::Local<v8::Object>::New(ddata->v8object); + } else if (ddata->v8object.IsEmpty() && + (ddata->v8objectid == m_id || // We own the QObject + ddata->v8objectid == 0 || // No one owns the QObject + !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted + + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + ddata->v8object = qPersistentNew<v8::Object>(rv); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + return rv; + + } else { + // If this object is tainted, we have to check to see if it is in our + // tainted object list + TaintedHash::Iterator iter = + ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end(); + bool found = iter != m_taintedObjects.end(); + + // If our tainted handle doesn't exist or has been collected, and there isn't + // a handle in the ddata, we can assume ownership of the ddata->v8object + if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + ddata->v8object = qPersistentNew<v8::Object>(rv); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + + if (found) { + delete (*iter); + m_taintedObjects.erase(iter); + } + + return rv; + } else if (!found) { + QV8QObjectInstance *instance = new QV8QObjectInstance(object, this); + iter = m_taintedObjects.insert(object, instance); + ddata->hasTaintedV8Object = true; + } + + if ((*iter)->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + (*iter)->v8object = qPersistentNew<v8::Object>(rv); + (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback); + } + + return v8::Local<v8::Object>::New((*iter)->v8object); + } +} + +QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object) +{ + if (object->IsFunction()) + return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object)); + + if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object)) + return qMakePair(resource->object.data(), resource->index); + + return qMakePair((QObject *)0, -1); +} + +QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function) +{ + v8::ScriptOrigin origin = function->GetScriptOrigin(); + if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) { + + // This is one of our special QObject method wrappers + v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject }; + v8::Local<v8::Value> data = function->Call(engine->global(), 1, args); + + if (data->IsArray()) { + v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data); + return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value()); + } + + // In theory this can't fall through, but I suppose V8 might run out of memory or something + } + + return qMakePair((QObject *)0, -1); +} + +class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject> +{ +public: + QV8QObjectConnectionList(QObject *object, QV8Engine *engine); + ~QV8QObjectConnectionList(); + + struct Connection { + Connection() + : needsDestroy(false) {} + Connection(const Connection &other) + : thisObject(other.thisObject), function(other.function), needsDestroy(false) {} + Connection &operator=(const Connection &other) { + thisObject = other.thisObject; + function = other.function; + needsDestroy = other.needsDestroy; + return *this; + } + + v8::Persistent<v8::Object> thisObject; + v8::Persistent<v8::Function> function; + + void dispose() { + qPersistentDispose(thisObject); + qPersistentDispose(function); + } + + bool needsDestroy; + }; + + struct ConnectionList : public QList<Connection> { + ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {} + int connectionsInUse; + bool connectionsNeedClean; + }; + + QV8Engine *engine; + + typedef QHash<int, ConnectionList> SlotHash; + SlotHash slotHash; + bool needsDestroy; + int inUse; + + virtual void objectDestroyed(QObject *); + virtual int qt_metacall(QMetaObject::Call, int, void **); +}; + +QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine) +: QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0) +{ +} + +QV8QObjectConnectionList::~QV8QObjectConnectionList() +{ + for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) { + QList<Connection> &connections = *iter; + for (int ii = 0; ii < connections.count(); ++ii) { + qPersistentDispose(connections[ii].thisObject); + qPersistentDispose(connections[ii].function); + } + } + slotHash.clear(); +} + +void QV8QObjectConnectionList::objectDestroyed(QObject *object) +{ + engine->qobjectWrapper()->m_connections.remove(object); + + if (inUse) + needsDestroy = true; + else + delete this; +} + +int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs) +{ + if (method == QMetaObject::InvokeMetaMethod) { + SlotHash::Iterator iter = slotHash.find(index); + if (iter == slotHash.end()) + return -1; + ConnectionList &connectionList = *iter; + if (connectionList.isEmpty()) + return -1; + + inUse++; + + connectionList.connectionsInUse++; + + QList<Connection> connections = connectionList; + + QVarLengthArray<int, 9> dummy; + int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + int argCount = argsTypes?argsTypes[0]:0; + QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount); + + for (int ii = 0; ii < argCount; ++ii) { + int type = argsTypes[ii + 1]; + if (type == qMetaTypeId<QVariant>()) { + args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); + } else { + args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1])); + } + } + + for (int ii = 0; ii < connections.count(); ++ii) { + Connection &connection = connections[ii]; + if (connection.needsDestroy) + continue; + + v8::TryCatch try_catch; + if (connection.thisObject.IsEmpty()) { + connection.function->Call(engine->global(), argCount, args.data()); + } else { + connection.function->Call(connection.thisObject, argCount, args.data()); + } + + if (try_catch.HasCaught()) { + QQmlError error; + error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName()))); + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + QQmlEnginePrivate::get(engine->engine())->warning(error); + } + } + + connectionList.connectionsInUse--; + if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) { + for (QList<Connection>::Iterator iter = connectionList.begin(); + iter != connectionList.end(); ) { + if (iter->needsDestroy) { + iter->dispose(); + iter = connectionList.erase(iter); + } else { + ++iter; + } + } + } + + inUse--; + if (inUse == 0 && needsDestroy) + delete this; + } + + return -1; +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("Function.prototype.connect: no arguments given"); + + QV8Engine *engine = V8ENGINE(); + + QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This()); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V8THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + if (!signalObject) + V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V8THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + v8::Local<v8::Value> functionValue; + v8::Local<v8::Value> functionThisValue; + + if (args.Length() == 1) { + functionValue = args[0]; + } else { + functionThisValue = args[0]; + functionValue = args[1]; + } + + if (!functionValue->IsFunction()) + V8THROW_ERROR("Function.prototype.connect: target is not a function"); + + if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject()) + V8THROW_ERROR("Function.prototype.connect: target this is not an object"); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections; + QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject); + if (iter == connections.end()) + iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine)); + + QV8QObjectConnectionList *connectionList = *iter; + QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex); + if (slotIter == connectionList->slotHash.end()) { + slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList()); + QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex); + } + + QV8QObjectConnectionList::Connection connection; + if (!functionThisValue.IsEmpty()) + connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject()); + connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue)); + + slotIter->append(connection); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("Function.prototype.disconnect: no arguments given"); + + QV8Engine *engine = V8ENGINE(); + + QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This()); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + if (!signalObject) + V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + v8::Local<v8::Value> functionValue; + v8::Local<v8::Value> functionThisValue; + + if (args.Length() == 1) { + functionValue = args[0]; + } else { + functionThisValue = args[0]; + functionValue = args[1]; + } + + if (!functionValue->IsFunction()) + V8THROW_ERROR("Function.prototype.disconnect: target is not a function"); + + if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject()) + V8THROW_ERROR("Function.prototype.disconnect: target this is not an object"); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections; + QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject); + if (iter == connectionsList.end()) + return v8::Undefined(); // Nothing to disconnect from + + QV8QObjectConnectionList *connectionList = *iter; + QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex); + if (slotIter == connectionList->slotHash.end()) + return v8::Undefined(); // Nothing to disconnect from + + QV8QObjectConnectionList::ConnectionList &connections = *slotIter; + + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue); + QPair<QObject *, int> functionData = ExtractQtMethod(engine, function); + + if (functionData.second != -1) { + // This is a QObject function wrapper + for (int ii = 0; ii < connections.count(); ++ii) { + QV8QObjectConnectionList::Connection &connection = connections[ii]; + + if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() && + (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) { + + QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function); + if (connectedFunctionData == functionData) { + // Match! + if (connections.connectionsInUse) { + connection.needsDestroy = true; + connections.connectionsNeedClean = true; + } else { + connection.dispose(); + connections.removeAt(ii); + } + return v8::Undefined(); + } + } + } + + } else { + // This is a normal JS function + for (int ii = 0; ii < connections.count(); ++ii) { + QV8QObjectConnectionList::Connection &connection = connections[ii]; + if (connection.function->StrictEquals(function) && + connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() && + (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) { + // Match! + if (connections.connectionsInUse) { + connection.needsDestroy = true; + connections.connectionsNeedClean = true; + } else { + connection.dispose(); + connections.removeAt(ii); + } + return v8::Undefined(); + } + } + } + + return v8::Undefined(); +} + +/*! + \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode) + + Get the \a property of \a object. Returns an empty handle if the property doesn't exist. + + Only searches for real properties of \a object (including methods), not attached properties etc. +*/ + +/* + \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode) + + Set the \a property of \a object to \a value. + + Returns true if the property was "set" - even if this results in an exception being thrown - + and false if the object has no such property. + + Only searches for real properties of \a object (including methods), not attached properties etc. +*/ + +namespace { +struct CallArgs +{ + CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {} + int Length() const { return _length; } + v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); } + +private: + int _length; + v8::Handle<v8::Object> *_args; +}; +} + +static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, + int *argTypes, QV8Engine *engine, CallArgs &callArgs) +{ + if (argCount > 0) { + + QVarLengthArray<CallArgument, 9> args(argCount + 1); + args[0].initAsType(returnType); + + for (int ii = 0; ii < argCount; ++ii) + args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]); + + QVarLengthArray<void *, 9> argData(args.count()); + for (int ii = 0; ii < args.count(); ++ii) + argData[ii] = args[ii].dataPtr(); + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); + + return args[0].toValue(engine); + + } else if (returnType != 0) { + + CallArgument arg; + arg.initAsType(returnType); + + void *args[] = { arg.dataPtr() }; + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + + return arg.toValue(engine); + + } else { + + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + return v8::Undefined(); + + } +} + +/*! + Returns the match score for converting \a actual to be of type \a conversionType. A + zero score means "perfect match" whereas a higher score is worse. + + The conversion table is copied out of the QtScript callQtMethod() function. +*/ +static int MatchScore(v8::Handle<v8::Value> actual, int conversionType) +{ + if (actual->IsNumber()) { + switch (conversionType) { + case QMetaType::Double: + return 0; + case QMetaType::Float: + return 1; + case QMetaType::LongLong: + case QMetaType::ULongLong: + return 2; + case QMetaType::Long: + case QMetaType::ULong: + return 3; + case QMetaType::Int: + case QMetaType::UInt: + return 4; + case QMetaType::Short: + case QMetaType::UShort: + return 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + return 6; + default: + return 10; + } + } else if (actual->IsString()) { + switch (conversionType) { + case QMetaType::QString: + return 0; + default: + return 10; + } + } else if (actual->IsBoolean()) { + switch (conversionType) { + case QMetaType::Bool: + return 0; + default: + return 10; + } + } else if (actual->IsDate()) { + switch (conversionType) { + case QMetaType::QDateTime: + return 0; + case QMetaType::QDate: + return 1; + case QMetaType::QTime: + return 2; + default: + return 10; + } + } else if (actual->IsRegExp()) { + switch (conversionType) { + case QMetaType::QRegExp: + return 0; + default: + return 10; + } + } else if (actual->IsArray()) { + switch (conversionType) { + case QMetaType::QStringList: + case QMetaType::QVariantList: + return 5; + default: + return 10; + } + } else if (actual->IsNull()) { + switch (conversionType) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + return 0; + default: { + const char *typeName = QMetaType::typeName(conversionType); + if (typeName && typeName[strlen(typeName) - 1] == '*') + return 0; + else + return 10; + } + } + } else if (actual->IsObject()) { + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual); + + QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource()); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + switch (conversionType) { + case QMetaType::QObjectStar: + return 0; + default: + return 10; + } + } else if (r && r->resourceType() == QV8ObjectResource::VariantType) { + if (conversionType == qMetaTypeId<QVariant>()) + return 0; + else if (r->engine->toVariant(actual, -1).userType() == conversionType) + return 0; + else + return 10; + } else { + return 10; + } + + } else { + return 10; + } +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; +} + +static QByteArray QMetaMethod_name(const QMetaMethod &m) +{ + QByteArray sig = m.signature(); + int paren = sig.indexOf('('); + if (paren == -1) + return sig; + else + return sig.left(paren); +} + +/*! +Returns the next related method, if one, or 0. +*/ +static const QQmlPropertyData * RelatedMethod(QObject *object, + const QQmlPropertyData *current, + QQmlPropertyData &dummy) +{ + QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache; + if (!current->isOverload()) + return 0; + + Q_ASSERT(!current->overrideIndexIsProperty); + + if (cache) { + return cache->method(current->overrideIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->overrideIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->overrideIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = QMetaMethod_name(method); + for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + if (methodName == QMetaMethod_name(mo->method(ii))) { + dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); + dummy.overrideIndexIsProperty = 0; + dummy.overrideIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + if (data.hasArguments()) { + + int *args = 0; + QVarLengthArray<int, 9> dummy; + QByteArray unknownTypeError; + + args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, + &unknownTypeError); + + if (!args) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } + + if (args[0] > callArgs.Length()) { + QString error = QLatin1String("Insufficient arguments"); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } + + return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs); + + } else { + + return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs); + + } +} + +/*! +Resolve the overloaded method to call. The algorithm works conceptually like this: + 1. Resolve the set of overloads it is *possible* to call. + Impossible overloads include those that have too many parameters or have parameters + of unknown type. + 2. Filter the set of overloads to only contain those with the closest number of + parameters. + For example, if we are called with 3 parameters and there are 2 overloads that + take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. + 3. Find the best remaining overload based on its match score. + If two or more overloads have the same match score, call the last one. The match + score is constructed by adding the matchScore() result for each of the parameters. +*/ +static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + int argumentCount = callArgs.Length(); + + const QQmlPropertyData *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QQmlPropertyData dummy; + const QQmlPropertyData *attempt = &data; + + do { + QVarLengthArray<int, 9> dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt->hasArguments()) { + typedef QQmlPropertyCache PC; + int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + + } while((attempt = RelatedMethod(object, attempt, dummy)) != 0); + + if (best) { + return CallPrecise(object, *best, engine, callArgs); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + const QQmlPropertyData *candidate = &data; + while (candidate) { + error += QLatin1String("\n ") + + QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature()); + candidate = RelatedMethod(object, candidate, dummy); + } + + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } +} + +static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>) +{ + QString result; + if (object) { + QString objectName = object->objectName(); + + result += QString::fromUtf8(object->metaObject()->className()); + result += QLatin1String("(0x"); + result += QString::number((quintptr)object,16); + + if (!objectName.isEmpty()) { + result += QLatin1String(", \""); + result += objectName; + result += QLatin1Char('\"'); + } + + result += QLatin1Char(')'); + } else { + result = QLatin1String("null"); + } + + return engine->toString(result); +} + +static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args) +{ + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata || ddata->indestructible) { + const char *error = "Invalid attempt to destroy() an indestructible object"; + v8::ThrowException(v8::Exception::Error(v8::String::New(error))); + return v8::Undefined(); + } + + int delay = 0; + if (argCount > 0) + delay = args->Get(0)->Uint32Value(); + + if (delay > 0) + QTimer::singleShot(delay, object, SLOT(deleteLater())); + else + object->deleteLater(); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args) +{ + // object, index, qmlglobal, argCount, args + Q_ASSERT(args.Length() == 5); + Q_ASSERT(args[0]->IsObject()); + + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject()); + + if (!resource) + return v8::Undefined(); + + int argCount = args[3]->Int32Value(); + v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]); + + // Special hack to return info about this closure. + if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) { + v8::Local<v8::Array> data = v8::Array::New(2); + data->Set(0, args[0]); + data->Set(1, args[1]); + return data; + } + + QObject *object = resource->object; + int index = args[1]->Int32Value(); + + if (!object) + return v8::Undefined(); + + if (index < 0) { + // Builtin functions + if (index == QOBJECT_TOSTRING_INDEX) { + return ToString(resource->engine, object, argCount, arguments); + } else if (index == QOBJECT_DESTROY_INDEX) { + return Destroy(resource->engine, object, argCount, arguments); + } else { + return v8::Undefined(); + } + } + + QQmlPropertyData method; + + if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) { + if (ddata->propertyCache) { + QQmlPropertyData *d = ddata->propertyCache->method(index); + if (!d) + return v8::Undefined(); + method = *d; + } + } + + if (method.coreIndex == -1) { + method.load(object->metaObject()->method(index)); + + if (method.coreIndex == -1) + return v8::Undefined(); + } + + if (method.isV8Function()) { + v8::Handle<v8::Value> rv; + v8::Handle<v8::Object> qmlglobal = args[2]->ToObject(); + + QQmlV8Function func(argCount, arguments, rv, qmlglobal, + resource->engine->contextWrapper()->context(qmlglobal), + resource->engine); + QQmlV8Function *funcptr = &func; + + void *args[] = { 0, &funcptr }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args); + + if (rv.IsEmpty()) return v8::Undefined(); + return rv; + } + + CallArgs callArgs(argCount, &arguments); + if (!method.isOverload()) { + return CallPrecise(object, method, resource->engine, callArgs); + } else { + return CallOverloaded(object, method, resource->engine, callArgs); + } +} + +CallArgument::CallArgument() +: type(QVariant::Invalid) +{ +} + +CallArgument::~CallArgument() +{ + cleanup(); +} + +void CallArgument::cleanup() +{ + if (type == QMetaType::QString) { + qstringPtr->~QString(); + } else if (type == -1 || type == QMetaType::QVariant) { + qvariantPtr->~QVariant(); + } else if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr->~QJSValue(); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + qlistPtr->~QList<QObject *>(); + } +} + +void *CallArgument::dataPtr() +{ + if (type == -1) + return qvariantPtr->data(); + else + return (void *)&allocData; +} + +void CallArgument::initAsType(int callType) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == 0) return; + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(); + type = callType; + } else if (callType == QMetaType::Int || + callType == QMetaType::UInt || + callType == QMetaType::Bool || + callType == QMetaType::Double || + callType == QMetaType::Float) { + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + type = callType; + } else if (callType == QMetaType::QString) { + qstringPtr = new (&allocData) QString(); + type = callType; + } else if (callType == QMetaType::QVariant) { + type = callType; + qvariantPtr = new (&allocData) QVariant(); + } else if (callType == qMetaTypeId<QList<QObject *> >()) { + type = callType; + qlistPtr = new (&allocData) QList<QObject *>(); + } else if (callType == qMetaTypeId<QQmlV8Handle>()) { + type = callType; + handlePtr = new (&allocData) QQmlV8Handle; + } else { + type = -1; + qvariantPtr = new (&allocData) QVariant(callType, (void *)0); + } +} + +void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value))); + type = qMetaTypeId<QJSValue>(); + } else if (callType == QMetaType::Int) { + intValue = quint32(value->Int32Value()); + type = callType; + } else if (callType == QMetaType::UInt) { + intValue = quint32(value->Uint32Value()); + type = callType; + } else if (callType == QMetaType::Bool) { + boolValue = value->BooleanValue(); + type = callType; + } else if (callType == QMetaType::Double) { + doubleValue = double(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::Float) { + floatValue = float(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value->IsNull() || value->IsUndefined()) + qstringPtr = new (&allocData) QString(); + else + qstringPtr = new (&allocData) QString(engine->toString(value->ToString())); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = engine->toQObject(value); + type = callType; + } else if (callType == qMetaTypeId<QVariant>()) { + qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1)); + type = callType; + } else if (callType == qMetaTypeId<QList<QObject*> >()) { + qlistPtr = new (&allocData) QList<QObject *>(); + if (value->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + uint32_t length = array->Length(); + for (uint32_t ii = 0; ii < length; ++ii) + qlistPtr->append(engine->toQObject(array->Get(ii))); + } else { + qlistPtr->append(engine->toQObject(value)); + } + type = callType; + } else if (callType == qMetaTypeId<QQmlV8Handle>()) { + handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value)); + type = callType; + } else { + qvariantPtr = new (&allocData) QVariant(); + type = -1; + + QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0; + QVariant v = engine->toVariant(value, -1); + + if (v.userType() == callType) { + *qvariantPtr = v; + } else if (v.canConvert((QVariant::Type)callType)) { + *qvariantPtr = v; + qvariantPtr->convert((QVariant::Type)callType); + } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) { + QObject *obj = ep->toQObject(v); + + if (obj) { + const QMetaObject *objMo = obj->metaObject(); + while (objMo && objMo != mo) objMo = objMo->superClass(); + if (!objMo) obj = 0; + } + + *qvariantPtr = QVariant(callType, &obj); + } else { + *qvariantPtr = QVariant(callType, (void *)0); + } + } +} + +v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine) +{ + if (type == qMetaTypeId<QJSValue>()) { + return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine); + } else if (type == QMetaType::Int) { + return v8::Integer::New(int(intValue)); + } else if (type == QMetaType::UInt) { + return v8::Integer::NewFromUnsigned(intValue); + } else if (type == QMetaType::Bool) { + return v8::Boolean::New(boolValue); + } else if (type == QMetaType::Double) { + return v8::Number::New(doubleValue); + } else if (type == QMetaType::Float) { + return v8::Number::New(floatValue); + } else if (type == QMetaType::QString) { + return engine->toString(*qstringPtr); + } else if (type == QMetaType::QObjectStar) { + QObject *object = qobjectPtr; + if (object) + QQmlData::get(object, true)->setImplicitDestructible(); + return engine->newQObject(object); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + QList<QObject *> &list = *qlistPtr; + v8::Local<v8::Array> array = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + array->Set(ii, engine->newQObject(list.at(ii))); + return array; + } else if (type == qMetaTypeId<QQmlV8Handle>()) { + return handlePtr->toHandle(); + } else if (type == -1 || type == qMetaTypeId<QVariant>()) { + QVariant value = *qvariantPtr; + v8::Handle<v8::Value> rv = engine->fromVariant(value); + if (QObject *object = engine->toQObject(rv)) + QQmlData::get(object, true)->setImplicitDestructible(); + return rv; + } else { + return v8::Undefined(); + } +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8qobjectwrapper_p.h b/src/qml/qml/v8/qv8qobjectwrapper_p.h new file mode 100644 index 0000000000..f7b965690b --- /dev/null +++ b/src/qml/qml/v8/qv8qobjectwrapper_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8QOBJECTWRAPPER_P_H +#define QV8QOBJECTWRAPPER_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/qmetatype.h> +#include <QtCore/qpair.h> +#include <QtCore/qhash.h> +#include <private/qv8_p.h> +#include <private/qhashedstring_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QV8Engine; +class QQmlData; +class QV8ObjectResource; +class QV8QObjectInstance; +class QV8QObjectConnectionList; +class QQmlPropertyCache; +class Q_QML_EXPORT QV8QObjectWrapper +{ +public: + QV8QObjectWrapper(); + ~QV8QObjectWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Handle<v8::Value> newQObject(QObject *object); + bool isQObject(v8::Handle<v8::Object>); + QObject *toQObject(v8::Handle<v8::Object>); + static QObject *toQObject(QV8ObjectResource *); + + enum RevisionMode { IgnoreRevision, CheckRevision }; + inline v8::Handle<v8::Value> getProperty(QObject *, const QHashedV8String &, RevisionMode); + inline bool setProperty(QObject *, const QHashedV8String &, v8::Handle<v8::Value>, RevisionMode); + +private: + friend class QQmlPropertyCache; + friend class QV8QObjectConnectionList; + friend class QV8QObjectInstance; + + v8::Local<v8::Object> newQObject(QObject *, QQmlData *, QV8Engine *); + static v8::Handle<v8::Value> GetProperty(QV8Engine *, QObject *, v8::Handle<v8::Value> *, + const QHashedV8String &, QV8QObjectWrapper::RevisionMode); + static bool SetProperty(QV8Engine *, QObject *, const QHashedV8String &, + v8::Handle<v8::Value>, QV8QObjectWrapper::RevisionMode); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Integer> Query(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Connect(const v8::Arguments &args); + static v8::Handle<v8::Value> Disconnect(const v8::Arguments &args); + static v8::Handle<v8::Value> Invoke(const v8::Arguments &args); + static QPair<QObject *, int> ExtractQtMethod(QV8Engine *, v8::Handle<v8::Function>); + static QPair<QObject *, int> ExtractQtSignal(QV8Engine *, v8::Handle<v8::Object>); + + QV8Engine *m_engine; + quint32 m_id; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_methodConstructor; + v8::Persistent<v8::Function> m_signalHandlerConstructor; + v8::Persistent<v8::String> m_toStringSymbol; + v8::Persistent<v8::String> m_destroySymbol; + QHashedV8String m_toStringString; + QHashedV8String m_destroyString; + v8::Persistent<v8::Object> m_hiddenObject; + QHash<QObject *, QV8QObjectConnectionList *> m_connections; + typedef QHash<QObject *, QV8QObjectInstance *> TaintedHash; + TaintedHash m_taintedObjects; +}; + +v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &string, + RevisionMode mode) +{ + QQmlData *dd = QQmlData::get(object, false); + if (!dd || !dd->propertyCache || m_toStringString == string || m_destroyString == string || + dd->propertyCache->property(string)) { + return GetProperty(m_engine, object, 0, string, mode); + } else { + return v8::Handle<v8::Value>(); + } +} + +bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &string, + v8::Handle<v8::Value> value, RevisionMode mode) +{ + QQmlData *dd = QQmlData::get(object, false); + if (!dd || !dd->propertyCache || m_toStringString == string || m_destroyString == string || + dd->propertyCache->property(string)) { + return SetProperty(m_engine, object, string, value, mode); + } else { + return false; + } +} + +QT_END_NAMESPACE + +#endif // QV8QOBJECTWRAPPER_P_H + + diff --git a/src/qml/qml/v8/qv8sequencewrapper.cpp b/src/qml/qml/v8/qv8sequencewrapper.cpp new file mode 100644 index 0000000000..883ed1b60c --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> + +#include "qv8sequencewrapper_p.h" +#include "qv8sequencewrapper_p_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +QV8SequenceWrapper::QV8SequenceWrapper() + : m_engine(0) +{ +} + +QV8SequenceWrapper::~QV8SequenceWrapper() +{ +} + +#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(); +void QV8SequenceWrapper::init(QV8Engine *engine) +{ + FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) + + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ValueOf)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetIndexedPropertyHandler(IndexedGetter, IndexedSetter, 0, IndexedDeleter, IndexedEnumerator); + ft->InstanceTemplate()->SetAccessor(v8::String::New("length"), LengthGetter, LengthSetter, + v8::Handle<v8::Value>(), v8::DEFAULT, + v8::PropertyAttribute(v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} +#undef REGISTER_QML_SEQUENCE_METATYPE + +void QV8SequenceWrapper::destroy() +{ + qPersistentDispose(m_toString); + qPersistentDispose(m_valueOf); + qPersistentDispose(m_constructor); +} + +bool QV8SequenceWrapper::isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs) +{ + Q_ASSERT(lhs && rhs && lhs->resourceType() == QV8ObjectResource::SequenceType && rhs->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *lr = static_cast<QV8SequenceResource *>(lhs); + QV8SequenceResource *rr = static_cast<QV8SequenceResource *>(rhs); + return lr->isEqual(rr); +} + +quint32 QV8SequenceWrapper::sequenceLength(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *sr = static_cast<QV8SequenceResource *>(r); + Q_ASSERT(sr); + return sr->lengthGetter(); +} + +#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + r = new QV8##ElementTypeName##SequenceResource(m_engine, object, propertyIndex); \ + } else + +v8::Local<v8::Object> QV8SequenceWrapper::newSequence(int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +{ + // This function is called when the property is a QObject Q_PROPERTY of + // the given sequence type. Internally we store a typed-sequence + // (as well as object ptr + property index for updated-read and write-back) + // and so access/mutate avoids variant conversion. + *succeeded = true; + QV8SequenceResource *r = 0; + FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return v8::Local<v8::Object>(); } + + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + rv->SetExternalResource(r); + rv->SetPrototype(v8::Array::New(1)->GetPrototype()); + return rv; +} +#undef NEW_REFERENCE_SEQUENCE + +#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + r = new QV8##ElementTypeName##SequenceResource(m_engine, v.value<SequenceType>()); \ + } else + +v8::Local<v8::Object> QV8SequenceWrapper::fromVariant(const QVariant& v, bool *succeeded) +{ + // This function is called when assigning a sequence value to a normal JS var + // in a JS block. Internally, we store a sequence of the specified type. + // Access and mutation is extremely fast since it will not need to modify any + // QObject property. + int sequenceType = v.userType(); + *succeeded = true; + QV8SequenceResource *r = 0; + FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return v8::Local<v8::Object>(); } + + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + rv->SetExternalResource(r); + rv->SetPrototype(v8::Array::New(1)->GetPrototype()); + return rv; +} +#undef NEW_COPY_SEQUENCE + +QVariant QV8SequenceWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *resource = static_cast<QV8SequenceResource *>(r); + return resource->toVariant(); +} + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (typeHint == qMetaTypeId<SequenceType>()) { \ + return QV8##ElementTypeName##SequenceResource::toVariant(m_engine, array, length, succeeded); \ + } else + +QVariant QV8SequenceWrapper::toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded) +{ + *succeeded = true; + uint32_t length = array->Length(); + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } +} +#undef SEQUENCE_TO_VARIANT + +v8::Handle<v8::Value> QV8SequenceWrapper::IndexedSetter(quint32 index, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedSetter(index, value); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::IndexedGetter(quint32 index, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedGetter(index); +} + +v8::Handle<v8::Boolean> QV8SequenceWrapper::IndexedDeleter(quint32 index, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedDeleter(index); +} + +v8::Handle<v8::Array> QV8SequenceWrapper::IndexedEnumerator(const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedEnumerator(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::LengthGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return v8::Integer::NewFromUnsigned(sr->lengthGetter()); +} + +void QV8SequenceWrapper::LengthSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + sr->lengthSetter(value); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ToStringGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ToString(const v8::Arguments &args) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This()); + Q_ASSERT(sr); + return sr->toString(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ValueOf(const v8::Arguments &args) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This()); + Q_ASSERT(sr); + v8::Handle<v8::Value> tostringValue = sr->toString(); + if (!tostringValue.IsEmpty()) + return tostringValue; + return v8::Integer::NewFromUnsigned(sr->lengthGetter()); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8sequencewrapper_p.h b/src/qml/qml/v8/qv8sequencewrapper_p.h new file mode 100644 index 0000000000..104135ff76 --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SEQUENCEWRAPPER_P_H +#define QV8SEQUENCEWRAPPER_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 <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8SequenceWrapper +{ +public: + QV8SequenceWrapper(); + ~QV8SequenceWrapper(); + + void init(QV8Engine *); + void destroy(); + + bool isEqual(QV8ObjectResource *lhs, const QVariant &rhs); + bool isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs); + quint32 sequenceLength(QV8ObjectResource *); + + v8::Local<v8::Object> newSequence(int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + v8::Local<v8::Object> fromVariant(const QVariant& v, bool *succeeded); + QVariant toVariant(QV8ObjectResource *); + QVariant toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded); + +private: + QV8Engine *m_engine; + + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::Function> m_valueOf; + + static v8::Handle<v8::Value> IndexedGetter(quint32 index, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> IndexedSetter(quint32 index, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Boolean> IndexedDeleter(quint32 index, const v8::AccessorInfo &info); + static v8::Handle<v8::Array> IndexedEnumerator(const v8::AccessorInfo &info); + static v8::Handle<v8::Value> LengthGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static void LengthSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> ValueOfGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ValueOf(const v8::Arguments &args); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info); +}; + + +QT_END_NAMESPACE + +#endif // QV8SEQUENCEWRAPPER_P_H diff --git a/src/qml/qml/v8/qv8sequencewrapper_p_p.h b/src/qml/qml/v8/qv8sequencewrapper_p_p.h new file mode 100644 index 0000000000..9d519809fa --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper_p_p.h @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SEQUENCEWRAPPER_P_P_H +#define QV8SEQUENCEWRAPPER_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/qqmlengine_p.h> +#include <private/qqmlmetatype_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QV8SequenceResource + \brief The abstract base class of the external resource used in sequence type objects + + Every sequence type object returned by QV8SequenceWrapper::fromVariant() or + QV8SequenceWrapper::newSequence() has a type-specific QV8SequenceResource which + contains the type name, the meta type ids of the sequence and sequence element + types, as well as either the sequence data (copy) or object pointer and property + index (reference) data associated with the sequence. + */ +class QV8SequenceResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(SequenceType); + +public: + virtual ~QV8SequenceResource() {} + + enum ObjectType { Reference, Copy }; + + virtual QVariant toVariant() = 0; + virtual bool isEqual(const QV8SequenceResource *v) = 0; + + virtual quint32 lengthGetter() = 0; + virtual void lengthSetter(v8::Handle<v8::Value> value) = 0; + virtual v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) = 0; + virtual v8::Handle<v8::Value> indexedGetter(quint32 index) = 0; + virtual v8::Handle<v8::Boolean> indexedDeleter(quint32 index) = 0; + virtual v8::Handle<v8::Array> indexedEnumerator() = 0; + virtual v8::Handle<v8::Value> toString() = 0; + + ObjectType objectType; + QByteArray typeName; + int sequenceMetaTypeId; + int elementMetaTypeId; + +protected: + QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId) + : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId) + { + } +}; + +// helper function to generate valid warnings if errors occur during sequence operations. +static void generateWarning(QV8Engine *engine, const QString& description) +{ + if (!engine) + return; + v8::Local<v8::StackTrace> currStack = v8::StackTrace::CurrentStackTrace(1); + if (currStack.IsEmpty()) + return; + v8::Local<v8::StackFrame> currFrame = currStack->GetFrame(0); + if (currFrame.IsEmpty()) + return; + + QQmlError retn; + retn.setDescription(description); + retn.setLine(currFrame->GetLineNumber()); + retn.setUrl(QUrl(engine->toString(currFrame->GetScriptName()))); + QQmlEnginePrivate::warning(engine->engine(), retn); +} + + +static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->Int32Value(); +} + +static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v) +{ + return v8::Integer::New(v); +} + +static QString convertIntToString(QV8Engine *, int v) +{ + return QString::number(v); +} + +static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->NumberValue(); +} + +static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v) +{ + return v8::Number::New(v); +} + +static QString convertRealToString(QV8Engine *, qreal v) +{ + return QString::number(v); +} + +static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->BooleanValue(); +} + +static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v) +{ + return v8::Boolean::New(v); +} + +static QString convertBoolToString(QV8Engine *, bool v) +{ + if (v) + return QLatin1String("true"); + return QLatin1String("false"); +} + +static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v) +{ + return e->toString(v->ToString()); +} + +static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v) +{ + return e->toString(v); +} + +static QString convertStringToString(QV8Engine *, const QString &v) +{ + return v; +} + +static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v) +{ + return e->toString(v->ToString()); +} + +static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v) +{ + return e->toString(v); +} + +static QString convertQStringToString(QV8Engine *, const QString &v) +{ + return v; +} + +static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v) +{ + QUrl u; + u.setEncodedUrl(e->toString(v->ToString()).toUtf8(), QUrl::TolerantMode); + return u; +} + +static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v) +{ + return e->toString(QLatin1String(v.toEncoded().data())); +} + +static QString convertUrlToString(QV8Engine *, const QUrl &v) +{ + return v.toString(); +} + + +/* + \internal + \class QV8<Type>SequenceResource + \brief The external resource used in sequence type objects + + Every sequence type object returned by QV8SequenceWrapper::newSequence() has + a QV8<Type>SequenceResource which contains a property index and a pointer + to the object which contains the property. + + Every sequence type object returned by QV8SequenceWrapper::fromVariant() has + a QV8<Type>SequenceResource which contains a copy of the sequence value. + Operations on the sequence are implemented directly in terms of that sequence data. + + There exists one QV8<Type>SequenceResource instance for every JavaScript Object + (sequence) instance returned from QV8SequenceWrapper::newSequence() or + QV8SequenceWrapper::fromVariant(). + */ + +// F(elementType, elementTypeName, sequenceType, defaultValue) +#define FOREACH_QML_SEQUENCE_TYPE(F) \ + F(int, Int, QList<int>, 0) \ + F(qreal, Real, QList<qreal>, 0.0) \ + F(bool, Bool, QList<bool>, false) \ + F(QString, String, QList<QString>, QString()) \ + F(QString, QString, QStringList, QString()) \ + F(QUrl, Url, QList<QUrl>, QUrl()) + +#define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \ + QT_END_NAMESPACE \ + Q_DECLARE_METATYPE(SequenceType) \ + QT_BEGIN_NAMESPACE \ + class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \ + public:\ + QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \ + : QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ + , object(obj), propertyIndex(propIdx) \ + { \ + } \ + QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \ + : QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ + , object(0), propertyIndex(-1), c(value) \ + { \ + } \ + ~QV8##SequenceElementTypeName##SequenceResource() \ + { \ + } \ + static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \ + { \ + SequenceType list; \ + for (uint32_t ii = 0; ii < length; ++ii) { \ + list.append(ConversionFromV8fn(e, array->Get(ii))); \ + } \ + *succeeded = true; \ + return QVariant::fromValue<SequenceType>(list); \ + } \ + QVariant toVariant() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return QVariant(); \ + loadReference(); \ + } \ + return QVariant::fromValue<SequenceType>(c); \ + } \ + bool isEqual(const QV8SequenceResource *v) \ + { \ + /* Note: two different sequences can never be equal (even if they */ \ + /* contain the same elements in the same order) in order to */ \ + /* maintain JavaScript semantics. However, if they both reference */ \ + /* the same QObject+propertyIndex, they are equal. */ \ + if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \ + if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ + const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ + return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \ + } \ + } else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \ + if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ + const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ + return (this == rhs); \ + } \ + } \ + return false; \ + } \ + quint32 lengthGetter() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return 0; \ + loadReference(); \ + } \ + return static_cast<quint32>(c.count()); \ + } \ + void lengthSetter(v8::Handle<v8::Value> value) \ + { \ + /* Get the new required length */ \ + if (value.IsEmpty() || !value->IsUint32()) \ + return; \ + quint32 newLength = value->Uint32Value(); \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (newLength > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during length set")); \ + return; \ + } \ + /* Read the sequence from the QObject property if we're a reference */ \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return; \ + loadReference(); \ + } \ + /* Determine whether we need to modify the sequence */ \ + qint32 newCount = static_cast<qint32>(newLength); \ + qint32 count = c.count(); \ + if (newCount == count) { \ + return; \ + } else if (newCount > count) { \ + /* according to ECMA262r3 we need to insert */ \ + /* undefined values increasing length to newLength. */ \ + /* We cannot, so we insert default-values instead. */ \ + while (newCount > count++) { \ + QT_TRY { \ + c.append(DefaultValue); \ + } QT_CATCH (std::bad_alloc &exception) { \ + generateWarning(engine, QString(QLatin1String(exception.what()) \ + + QLatin1String(" during length set"))); \ + return; /* failed; don't write back any result. */ \ + } \ + } \ + } else { \ + /* according to ECMA262r3 we need to remove */ \ + /* elements until the sequence is the required length. */ \ + while (newCount < count) { \ + count--; \ + c.removeAt(count); \ + } \ + } \ + /* write back if required. */ \ + if (objectType == QV8SequenceResource::Reference) { \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + storeReference(); \ + } \ + return; \ + } \ + v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during indexed set")); \ + return v8::Undefined(); \ + } \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + /* modify the sequence */ \ + SequenceElementType elementValue = ConversionFromV8fn(engine, value); \ + qint32 count = c.count(); \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx == count) { \ + c.append(elementValue); \ + } else if (signedIdx < count) { \ + c[index] = elementValue; \ + } else { \ + /* according to ECMA262r3 we need to insert */ \ + /* the value at the given index, increasing length to index+1. */ \ + QT_TRY { \ + while (signedIdx > count++) { \ + c.append(DefaultValue); \ + } \ + c.append(elementValue); \ + } QT_CATCH (std::bad_alloc &exception) { \ + generateWarning(engine, QString(QLatin1String(exception.what()) \ + + QLatin1String(" during indexed set"))); \ + return v8::Undefined(); /* failed; don't write back any result. */ \ + } \ + } \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + if (objectType == QV8SequenceResource::Reference) \ + storeReference(); \ + return value; \ + } \ + v8::Handle<v8::Value> indexedGetter(quint32 index) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during indexed get")); \ + return v8::Undefined(); \ + } \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + qint32 count = c.count(); \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx < count) \ + return ConversionToV8fn(engine, c.at(signedIdx)); \ + return v8::Undefined(); \ + } \ + v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) \ + return v8::Boolean::New(false); \ + /* Read in the sequence from the QObject */ \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Boolean::New(false); \ + loadReference(); \ + } \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx < c.count()) { \ + /* according to ECMA262r3 it should be Undefined, */ \ + /* but we cannot, so we insert a default-value instead. */ \ + c.replace(signedIdx, DefaultValue); \ + if (objectType == QV8SequenceResource::Reference) { \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + storeReference(); \ + } \ + return v8::Boolean::New(true); \ + } \ + return v8::Boolean::New(false); \ + } \ + v8::Handle<v8::Array> indexedEnumerator() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Handle<v8::Array>(); \ + loadReference(); \ + } \ + qint32 count = c.count(); \ + v8::Local<v8::Array> retn = v8::Array::New(count); \ + for (qint32 i = 0; i < count; ++i) { \ + retn->Set(static_cast<quint32>(i), v8::Integer::NewFromUnsigned(static_cast<quint32>(i))); \ + } \ + return retn; \ + } \ + v8::Handle<v8::Value> toString() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + QString str; \ + qint32 count = c.count(); \ + for (qint32 i = 0; i < count; ++i) { \ + str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \ + } \ + str.chop(1); \ + return engine->toString(str); \ + } \ + void loadReference() \ + { \ + Q_ASSERT(object); \ + Q_ASSERT(objectType == QV8SequenceResource::Reference); \ + void *a[] = { &c, 0 }; \ + QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \ + } \ + void storeReference() \ + { \ + Q_ASSERT(object); \ + Q_ASSERT(objectType == QV8SequenceResource::Reference); \ + int status = -1; \ + QQmlPropertyPrivate::WriteFlags flags = \ + QQmlPropertyPrivate::DontRemoveBinding; \ + void *a[] = { &c, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \ + } \ + private: \ + QQmlGuard<QObject> object; \ + int propertyIndex; \ + SequenceType c; \ + }; + +#define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \ + QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString) + +FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE) +#undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE +#undef QML_SEQUENCE_TYPE_RESOURCE + +QT_END_NAMESPACE + +#endif // QV8SEQUENCEWRAPPER_P_P_H diff --git a/src/qml/qml/v8/qv8sqlerrors.cpp b/src/qml/qml/v8/qv8sqlerrors.cpp new file mode 100644 index 0000000000..8c5856ea18 --- /dev/null +++ b/src/qml/qml/v8/qv8sqlerrors.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8sqlerrors_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +void qt_add_sqlexceptions(QV8Engine *engine) +{ + // SQL Exception + v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + + v8::Local<v8::Object> sqlexception = v8::Object::New(); + sqlexception->Set(v8::String::New("UNKNOWN_ERR"), v8::Integer::New(SQLEXCEPTION_UNKNOWN_ERR), attributes); + sqlexception->Set(v8::String::New("DATABASE_ERR"), v8::Integer::New(SQLEXCEPTION_DATABASE_ERR), attributes); + sqlexception->Set(v8::String::New("VERSION_ERR"), v8::Integer::New(SQLEXCEPTION_VERSION_ERR), attributes); + sqlexception->Set(v8::String::New("TOO_LARGE_ERR"), v8::Integer::New(SQLEXCEPTION_TOO_LARGE_ERR), attributes); + sqlexception->Set(v8::String::New("QUOTA_ERR"), v8::Integer::New(SQLEXCEPTION_QUOTA_ERR), attributes); + sqlexception->Set(v8::String::New("SYNTAX_ERR"), v8::Integer::New(SQLEXCEPTION_SYNTAX_ERR), attributes); + sqlexception->Set(v8::String::New("CONSTRAINT_ERR"), v8::Integer::New(SQLEXCEPTION_CONSTRAINT_ERR), attributes); + sqlexception->Set(v8::String::New("TIMEOUT_ERR"), v8::Integer::New(SQLEXCEPTION_TIMEOUT_ERR), attributes); + engine->global()->Set(v8::String::New("SQLException"), sqlexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8sqlerrors_p.h b/src/qml/qml/v8/qv8sqlerrors_p.h new file mode 100644 index 0000000000..c799be6e7c --- /dev/null +++ b/src/qml/qml/v8/qv8sqlerrors_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SQLERRORS_P_H +#define QV8SQLERRORS_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> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +#define SQLEXCEPTION_UNKNOWN_ERR 1 +#define SQLEXCEPTION_DATABASE_ERR 2 +#define SQLEXCEPTION_VERSION_ERR 3 +#define SQLEXCEPTION_TOO_LARGE_ERR 4 +#define SQLEXCEPTION_QUOTA_ERR 5 +#define SQLEXCEPTION_SYNTAX_ERR 6 +#define SQLEXCEPTION_CONSTRAINT_ERR 7 +#define SQLEXCEPTION_TIMEOUT_ERR 8 + +class QV8Engine; +void qt_add_sqlexceptions(QV8Engine *engine); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8SQLERRORS_P_H diff --git a/src/qml/qml/v8/qv8stringwrapper.cpp b/src/qml/qml/v8/qv8stringwrapper.cpp new file mode 100644 index 0000000000..d4abbdc60b --- /dev/null +++ b/src/qml/qml/v8/qv8stringwrapper.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8stringwrapper_p.h" +#include "qjsconverter_p.h" +#include "qjsconverter_impl_p.h" + +QT_BEGIN_NAMESPACE + +QV8StringWrapper::QV8StringWrapper() +{ +} + +QV8StringWrapper::~QV8StringWrapper() +{ +} + +void QV8StringWrapper::init() +{ +} + +void QV8StringWrapper::destroy() +{ +} + +v8::Local<v8::String> QV8StringWrapper::toString(const QString &qstr) +{ + return QJSConverter::toString(qstr); +} + +QString QV8StringWrapper::toString(v8::Handle<v8::String> jsstr) +{ + if (jsstr.IsEmpty()) { + return QString(); + } else { + return QJSConverter::toString(jsstr); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8stringwrapper_p.h b/src/qml/qml/v8/qv8stringwrapper_p.h new file mode 100644 index 0000000000..1609720298 --- /dev/null +++ b/src/qml/qml/v8/qv8stringwrapper_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLV8STRINGWRAPPER_P_H +#define QQMLV8STRINGWRAPPER_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 <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_EXPORT QV8StringWrapper +{ +public: + QV8StringWrapper(); + ~QV8StringWrapper(); + + void init(); + void destroy(); + + v8::Local<v8::String> toString(const QString &); + QString toString(v8::Handle<v8::String>); +}; + +QT_END_NAMESPACE + +#endif // QQMLV8STRINGWRAPPER_P_H diff --git a/src/qml/qml/v8/qv8typewrapper.cpp b/src/qml/qml/v8/qv8typewrapper.cpp new file mode 100644 index 0000000000..dbf369e678 --- /dev/null +++ b/src/qml/qml/v8/qv8typewrapper.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_p.h> + +#include <private/qjsvalue_p.h> +#include <private/qscript_impl_p.h> + +QT_BEGIN_NAMESPACE + +class QV8TypeResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(TypeType); + +public: + QV8TypeResource(QV8Engine *engine); + virtual ~QV8TypeResource(); + + QV8TypeWrapper::TypeNameMode mode; + + QQmlGuard<QObject> object; + + QQmlType *type; + QQmlTypeNameCache *typeNamespace; + const void *importNamespace; +}; + +QV8TypeResource::QV8TypeResource(QV8Engine *engine) +: QV8ObjectResource(engine), mode(QV8TypeWrapper::IncludeEnums), type(0), typeNamespace(0), importNamespace(0) +{ +} + +QV8TypeResource::~QV8TypeResource() +{ + if (typeNamespace) typeNamespace->release(); +} + +QV8TypeWrapper::QV8TypeWrapper() +: m_engine(0) +{ +} + +QV8TypeWrapper::~QV8TypeWrapper() +{ +} + +void QV8TypeWrapper::destroy() +{ + qPersistentDispose(m_constructor); +} + +void QV8TypeWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} + +// Returns a type wrapper for type t on o. This allows access of enums, and attached properties. +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlType *t, TypeNameMode mode) +{ + Q_ASSERT(t); + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8TypeResource *r = new QV8TypeResource(m_engine); + r->mode = mode; r->object = o; r->type = t; + rv->SetExternalResource(r); + return rv; +} + +// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a +// namespace. +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlTypeNameCache *t, + const void *importNamespace, TypeNameMode mode) +{ + Q_ASSERT(t); + Q_ASSERT(importNamespace); + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8TypeResource *r = new QV8TypeResource(m_engine); + t->addref(); + r->mode = mode; r->object = o; r->typeNamespace = t; r->importNamespace = importNamespace; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8TypeWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::TypeType); + QV8TypeResource *resource = static_cast<QV8TypeResource *>(r); + QV8Engine *v8engine = resource->engine; + + if (resource->typeNamespace) { + if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } + + if (moduleApi->qobjectApi) { + return QVariant::fromValue<QObject*>(moduleApi->qobjectApi); + } + } + } + + // only QObject Module API can be converted to a variant. + return QVariant(); +} + +v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return v8::Undefined(); + + QV8Engine *v8engine = resource->engine; + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + if (resource->type) { + QQmlType *type = resource->type; + + if (QV8Engine::startsWithUpper(property)) { + int value = type->enumValue(propertystring); + if (-1 != value) + return v8::Integer::New(value); + + // Fall through to return empty handle + + } else if (resource->object) { + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + return v8engine->qobjectWrapper()->getProperty(ao, propertystring, + QV8QObjectWrapper::IgnoreRevision); + + // Fall through to return empty handle + } + + // Fall through to return empty handle + + } else if (resource->typeNamespace) { + Q_ASSERT(resource->importNamespace); + QQmlTypeNameCache::Result r = resource->typeNamespace->query(propertystring, + resource->importNamespace); + + if (r.isValid()) { + if (r.type) { + return v8engine->typeWrapper()->newObject(object, r.type, resource->mode); + } else if (r.scriptIndex != -1) { + int index = r.scriptIndex; + QQmlContextData *context = v8engine->callingContext(); + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + } + + return v8::Undefined(); + } else if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } + + if (moduleApi->qobjectApi) { + // check for enum value + if (QV8Engine::startsWithUpper(property)) { + if (resource->mode == IncludeEnums) { + QString name = v8engine->toString(property); + + // ### Optimize + QByteArray enumName = name.toUtf8(); + const QMetaObject *metaObject = moduleApi->qobjectApi->metaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + bool ok; + int value = e.keyToValue(enumName.constData(), &ok); + if (ok) + return v8::Integer::New(value); + } + } + } + + // check for property. + v8::Handle<v8::Value> rv = v8engine->qobjectWrapper()->getProperty(moduleApi->qobjectApi, propertystring, QV8QObjectWrapper::IgnoreRevision); + return rv; + } else if (!moduleApi->scriptApi.isUndefined()) { + // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. + QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi); + QScopedPointer<QJSValuePrivate> propertyValue(apiprivate->property(property).give()); + return propertyValue->asV8Value(v8engine); + } else { + return v8::Handle<v8::Value>(); + } + } + + // Fall through to return empty handle + + } else { + Q_ASSERT(!"Unreachable"); + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return value; + + QV8Engine *v8engine = resource->engine; + + QHashedV8String propertystring(property); + + if (resource->type && resource->object) { + QQmlType *type = resource->type; + QObject *object = resource->object; + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + v8engine->qobjectWrapper()->setProperty(ao, propertystring, value, + QV8QObjectWrapper::IgnoreRevision); + } else if (resource->typeNamespace) { + if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } + + if (moduleApi->qobjectApi) { + v8engine->qobjectWrapper()->setProperty(moduleApi->qobjectApi, propertystring, value, + QV8QObjectWrapper::IgnoreRevision); + } else if (!moduleApi->scriptApi.isUndefined()) { + QScopedPointer<QJSValuePrivate> setvalp(new QJSValuePrivate(v8engine, value)); + QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi); + if (apiprivate->propertyFlags(property) & QJSValuePrivate::ReadOnly) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); + } else { + apiprivate->setProperty(property, setvalp.data()); + } + } + } + } + + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8typewrapper_p.h b/src/qml/qml/v8/qv8typewrapper_p.h new file mode 100644 index 0000000000..8b658da6fb --- /dev/null +++ b/src/qml/qml/v8/qv8typewrapper_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8TYPEWRAPPER_P_H +#define QV8TYPEWRAPPER_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 <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QV8Engine; +class QQmlType; +class QQmlTypeNameCache; +class QV8TypeWrapper +{ +public: + QV8TypeWrapper(); + ~QV8TypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + enum TypeNameMode { IncludeEnums, ExcludeEnums }; + v8::Local<v8::Object> newObject(QObject *, QQmlType *, TypeNameMode = IncludeEnums); + v8::Local<v8::Object> newObject(QObject *, QQmlTypeNameCache *, const void *, + TypeNameMode = IncludeEnums); + QVariant toVariant(QV8ObjectResource *); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8TYPEWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp new file mode 100644 index 0000000000..54d871d5f0 --- /dev/null +++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8valuetypewrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlvaluetype_p.h> +#include <private/qqmlbinding_p.h> + +QT_BEGIN_NAMESPACE + +class QV8ValueTypeResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ValueTypeType); + +public: + enum ObjectType { Reference, Copy }; + + QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType); + + ObjectType objectType; + QQmlValueType *type; +}; + +class QV8ValueTypeReferenceResource : public QV8ValueTypeResource +{ +public: + QV8ValueTypeReferenceResource(QV8Engine *engine); + + QQmlGuard<QObject> object; + int property; +}; + +class QV8ValueTypeCopyResource : public QV8ValueTypeResource +{ +public: + QV8ValueTypeCopyResource(QV8Engine *engine); + + QVariant value; +}; + +QV8ValueTypeResource::QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType) +: QV8ObjectResource(engine), objectType(objectType) +{ +} + +QV8ValueTypeReferenceResource::QV8ValueTypeReferenceResource(QV8Engine *engine) +: QV8ValueTypeResource(engine, Reference) +{ +} + +QV8ValueTypeCopyResource::QV8ValueTypeCopyResource(QV8Engine *engine) +: QV8ValueTypeResource(engine, Copy) +{ +} + +QV8ValueTypeWrapper::QV8ValueTypeWrapper() +: m_engine(0) +{ +} + +QV8ValueTypeWrapper::~QV8ValueTypeWrapper() +{ +} + +void QV8ValueTypeWrapper::destroy() +{ + qPersistentDispose(m_toString); + qPersistentDispose(m_constructor); + qPersistentDispose(m_toStringSymbol); +} + +static quint32 toStringHash = -1; + +void QV8ValueTypeWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + + m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString")); + m_toStringString = QHashedV8String(m_toStringSymbol); + toStringHash = m_toStringString.hash(); +} + +v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QQmlValueType *type) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ValueTypeReferenceResource *r = new QV8ValueTypeReferenceResource(m_engine); + r->type = type; r->object = object; r->property = property; + rv->SetExternalResource(r); + return rv; +} + +v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(const QVariant &value, QQmlValueType *type) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ValueTypeCopyResource *r = new QV8ValueTypeCopyResource(m_engine); + r->type = type; r->value = value; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj); + if (r) return toVariant(r); + else return QVariant(); +} + +QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType); + QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r); + + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + + if (reference->object) { + reference->type->read(reference->object, reference->property); + return reference->type->value(); + } else { + return QVariant(); + } + + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + + return copy->value; + } +} + +bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType); + QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r); + + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + if (reference->object) { + reference->type->read(reference->object, reference->property); + return reference->type->isEqual(value); + } else { + return false; + } + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + return (value == copy->value); + } +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args) +{ + QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This()); + if (resource) { + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + if (reference->object) { + reference->type->read(reference->object, reference->property); + return resource->engine->toString(resource->type->toString()); + } else { + return v8::Undefined(); + } + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + QString result = copy->value.toString(); + if (result.isEmpty() && !copy->value.canConvert(QVariant::String)) { + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(copy->value.typeName())); + } + return resource->engine->toString(result); + } + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This()); + if (!r) return v8::Handle<v8::Value>(); + + QHashedV8String propertystring(property); + + { + // Comparing the hash first actually makes a measurable difference here, at least on x86 + quint32 hash = propertystring.hash(); + if (hash == toStringHash && + r->engine->valueTypeWrapper()->m_toStringString == propertystring) { + return r->engine->valueTypeWrapper()->m_toString; + } + } + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + QQmlData *ddata = QQmlData::get(r->type, false); + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(propertystring); + else + result = QQmlPropertyCache::property(r->engine->engine(), r->type, + propertystring, local); + } + + if (!result) + return v8::Handle<v8::Value>(); + + if (r->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r); + + if (!reference->object) + return v8::Handle<v8::Value>(); + + r->type->read(reference->object, reference->property); + } else { + Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r); + + r->type->setValue(copy->value); + } + +#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ + if (result->propType == metatype) { \ + cpptype v; \ + void *args[] = { &v, 0 }; \ + r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \ + return constructor(v); \ + } + + // These four types are the most common used by the value type wrappers + VALUE_TYPE_LOAD(QMetaType::QReal, qreal, v8::Number::New); + VALUE_TYPE_LOAD(QMetaType::Int, int, v8::Integer::New); + VALUE_TYPE_LOAD(QMetaType::QString, QString, r->engine->toString); + VALUE_TYPE_LOAD(QMetaType::Bool, bool, v8::Boolean::New); + + QVariant v(result->propType, (void *)0); + void *args[] = { v.data(), 0 }; + r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); + return r->engine->fromVariant(v); +#undef VALUE_TYPE_ACCESSOR +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This()); + if (!r) return value; + + QByteArray propName = r->engine->toString(property).toUtf8(); + int index = r->type->metaObject()->indexOfProperty(propName.constData()); + if (index == -1) + return value; + + if (r->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r); + + if (!reference->object || + !reference->object->metaObject()->property(reference->property).isWritable()) + return value; + + r->type->read(reference->object, reference->property); + QMetaProperty p = r->type->metaObject()->property(index); + + QQmlBinding *newBinding = 0; + + if (value->IsFunction()) { + QQmlContextData *context = r->engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + QQmlPropertyData cacheData; + cacheData.setFlags(QQmlPropertyData::IsWritable | + QQmlPropertyData::IsValueTypeVirtual); + cacheData.propType = reference->object->metaObject()->property(reference->property).userType(); + cacheData.coreIndex = reference->property; + cacheData.valueTypeFlags = 0; + cacheData.valueTypeCoreIndex = index; + cacheData.valueTypePropType = p.userType(); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, + (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + QString url = r->engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, reference->object, context); + newBinding->setSourceLocation(url, lineNumber, columnNumber); + newBinding->setTarget(reference->object, cacheData, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding); + if (oldBinding) + oldBinding->destroy(); + + if (!value->IsFunction()) { + QVariant v = r->engine->toVariant(value, -1); + + if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double) + v = v.toInt(); + + p.write(reference->type, v); + + reference->type->write(reference->object, reference->property, 0); + } + + } else { + Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r); + + QVariant v = r->engine->toVariant(value, -1); + + r->type->setValue(copy->value); + QMetaProperty p = r->type->metaObject()->property(index); + p.write(r->type, v); + copy->value = r->type->value(); + } + + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8valuetypewrapper_p.h b/src/qml/qml/v8/qv8valuetypewrapper_p.h new file mode 100644 index 0000000000..b80d3cbbba --- /dev/null +++ b/src/qml/qml/v8/qv8valuetypewrapper_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VALUETYPEWRAPPER_P_H +#define QV8VALUETYPEWRAPPER_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 <QtQml/qqmllist.h> +#include <private/qv8_p.h> +#include <private/qhashedstring_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QQmlValueType; +class QV8ValueTypeWrapper +{ +public: + QV8ValueTypeWrapper(); + ~QV8ValueTypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> newValueType(QObject *, int, QQmlValueType *); + v8::Local<v8::Object> newValueType(const QVariant &, QQmlValueType *); + + QVariant toVariant(v8::Handle<v8::Object>); + QVariant toVariant(QV8ObjectResource *); + + static bool isEqual(QV8ObjectResource *, const QVariant& value); + +private: + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::String> m_toStringSymbol; + QHashedV8String m_toStringString; +}; + +QT_END_NAMESPACE + +#endif // QV8VALUETYPEWRAPPER_P_H + + diff --git a/src/qml/qml/v8/qv8variantresource_p.h b/src/qml/qml/v8/qv8variantresource_p.h new file mode 100644 index 0000000000..0b6328cb54 --- /dev/null +++ b/src/qml/qml/v8/qv8variantresource_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VARIANTRESOURCE_P_H +#define QV8VARIANTRESOURCE_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 <private/qv8_p.h> +#include <private/qv8engine_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +class QV8VariantResource : public QV8ObjectResource, + public QQmlEnginePrivate::ScarceResourceData +{ + V8_RESOURCE_TYPE(VariantType) + +public: + QV8VariantResource(QV8Engine *engine, const QVariant &data); + + void addVmePropertyReference(); + void removeVmePropertyReference(); + + bool m_isScarceResource; + int m_vmePropertyReferenceCount; +}; + +QT_END_NAMESPACE + +#endif // QV8VARIANTRESOURCE_P_H + diff --git a/src/qml/qml/v8/qv8variantwrapper.cpp b/src/qml/qml/v8/qv8variantwrapper.cpp new file mode 100644 index 0000000000..4b1fc643f6 --- /dev/null +++ b/src/qml/qml/v8/qv8variantwrapper.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8variantwrapper_p.h" +#include "qv8variantresource_p.h" +#include "qv8engine_p.h" +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QV8VariantResource::QV8VariantResource(QV8Engine *engine, const QVariant &data) +: QV8ObjectResource(engine), QQmlEnginePrivate::ScarceResourceData(data), m_isScarceResource(false), m_vmePropertyReferenceCount(0) +{ +} + +void QV8VariantResource::addVmePropertyReference() +{ + if (m_isScarceResource && ++m_vmePropertyReferenceCount == 1) { + // remove from the ep->scarceResources list + // since it is now no longer eligible to be + // released automatically by the engine. + node.remove(); + } +} + +void QV8VariantResource::removeVmePropertyReference() +{ + if (m_isScarceResource && --m_vmePropertyReferenceCount == 0) { + // and add to the ep->scarceResources list + // since it is now eligible to be released + // automatically by the engine. + QQmlEnginePrivate::get(engine->engine())->scarceResources.insert(this); + } +} + +QV8VariantWrapper::QV8VariantWrapper() +: m_engine(0) +{ +} + +QV8VariantWrapper::~QV8VariantWrapper() +{ +} + +void QV8VariantWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ValueOf)->GetFunction()); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + m_preserve = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(Preserve)->GetFunction()); + m_destroy = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(Destroy)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + ft->InstanceTemplate()->SetAccessor(v8::String::New("preserve"), PreserveGetter, 0, + m_preserve, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("destroy"), DestroyGetter, 0, + m_destroy, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + m_scarceConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + +} + +void QV8VariantWrapper::destroy() +{ + qPersistentDispose(m_valueOf); + qPersistentDispose(m_toString); + qPersistentDispose(m_destroy); + qPersistentDispose(m_preserve); + qPersistentDispose(m_scarceConstructor); + qPersistentDispose(m_constructor); +} + +v8::Local<v8::Object> QV8VariantWrapper::newVariant(const QVariant &value) +{ + bool scarceResource = value.type() == QVariant::Pixmap || + value.type() == QVariant::Image; + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv; + QV8VariantResource *r = new QV8VariantResource(m_engine, value); + + if (scarceResource) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine->engine()); + Q_ASSERT(ep->scarceResourcesRefCount); + rv = m_scarceConstructor->NewInstance(); + r->m_isScarceResource = true; + ep->scarceResources.insert(r); + } else { + rv = m_constructor->NewInstance(); + } + + rv->SetExternalResource(r); + return rv; +} + +bool QV8VariantWrapper::isVariant(v8::Handle<v8::Value> value) +{ + return value->IsObject() && v8_resource_cast<QV8VariantResource>(value->ToObject()); +} + +QVariant QV8VariantWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(obj); + return r?r->data:QVariant(); +} + +QVariant QV8VariantWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::VariantType); + return static_cast<QV8VariantResource *>(r)->data; +} + +QVariant &QV8VariantWrapper::variantValue(v8::Handle<v8::Value> value) +{ + Q_ASSERT(isVariant(value)); + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(value->ToObject()); + return static_cast<QV8VariantResource *>(r)->data; +} + +v8::Handle<v8::Value> QV8VariantWrapper::Getter(v8::Local<v8::String> /* property */, + const v8::AccessorInfo & /* info */) +{ + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Setter(v8::Local<v8::String> /* property */, + v8::Local<v8::Value> value, + const v8::AccessorInfo & /* info */) +{ + return value; +} + +v8::Handle<v8::Value> QV8VariantWrapper::PreserveGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::DestroyGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Preserve(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + resource->node.remove(); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Destroy(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + resource->data = QVariant(); + resource->node.remove(); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ToString(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + QString result = resource->data.toString(); + if (result.isEmpty() && !resource->data.canConvert(QVariant::String)) + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(resource->data.typeName())); + return resource->engine->toString(result); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8VariantWrapper::ValueOf(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + QVariant v = resource->data; + switch (v.type()) { + case QVariant::Invalid: + return v8::Undefined(); + case QVariant::String: + return resource->engine->toString(v.toString()); + case QVariant::Int: + case QVariant::Double: + case QVariant::UInt: + return v8::Number::New(v.toDouble()); + case QVariant::Bool: + return v8::Boolean::New(v.toBool()); + default: + break; + } + } + return args.This(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8variantwrapper_p.h b/src/qml/qml/v8/qv8variantwrapper_p.h new file mode 100644 index 0000000000..877155c8ca --- /dev/null +++ b/src/qml/qml/v8/qv8variantwrapper_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VARIANTWRAPPER_P_H +#define QV8VARIANTWRAPPER_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 <QtQml/qqmllist.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8VariantWrapper +{ +public: + QV8VariantWrapper(); + ~QV8VariantWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> newVariant(const QVariant &); + bool isVariant(v8::Handle<v8::Value>); + static QVariant toVariant(v8::Handle<v8::Object>); + static QVariant toVariant(QV8ObjectResource *); + QVariant &variantValue(v8::Handle<v8::Value>); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> PreserveGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> DestroyGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Preserve(const v8::Arguments &args); + static v8::Handle<v8::Value> Destroy(const v8::Arguments &args); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> ValueOf(const v8::Arguments &args); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_scarceConstructor; + v8::Persistent<v8::Function> m_preserve; + v8::Persistent<v8::Function> m_destroy; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::Function> m_valueOf; +}; + +QT_END_NAMESPACE + +#endif // QV8VARIANTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8worker.cpp b/src/qml/qml/v8/qv8worker.cpp new file mode 100644 index 0000000000..6ea527166c --- /dev/null +++ b/src/qml/qml/v8/qv8worker.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8worker_p.h" + +#include <private/qquicklistmodel_p.h> +#include <private/qquicklistmodelworkeragent_p.h> + +QT_BEGIN_NAMESPACE + +// We allow the following JavaScript types to be passed between the main and +// the secondary thread: +// + undefined +// + null +// + Boolean +// + String +// + Function +// + Array +// + "Simple" Objects +// + Number +// + Date +// + RegExp +// <quint8 type><quint24 size><data> + +enum Type { + WorkerUndefined, + WorkerNull, + WorkerTrue, + WorkerFalse, + WorkerString, + WorkerFunction, + WorkerArray, + WorkerObject, + WorkerInt32, + WorkerUint32, + WorkerNumber, + WorkerDate, + WorkerRegexp, + WorkerListModel, + WorkerSequence +}; + +static inline quint32 valueheader(Type type, quint32 size = 0) +{ + return quint8(type) << 24 | (size & 0xFFFFFF); +} + +static inline Type headertype(quint32 header) +{ + return (Type)(header >> 24); +} + +static inline quint32 headersize(quint32 header) +{ + return header & 0xFFFFFF; +} + +static inline void push(QByteArray &data, quint32 value) +{ + data.append((const char *)&value, sizeof(quint32)); +} + +static inline void push(QByteArray &data, double value) +{ + data.append((const char *)&value, sizeof(double)); +} + +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + +static inline void reserve(QByteArray &data, int extra) +{ + data.reserve(data.size() + extra); +} + +static inline quint32 popUint32(const char *&data) +{ + quint32 rv = *((quint32 *)data); + data += sizeof(quint32); + return rv; +} + +static inline double popDouble(const char *&data) +{ + double rv = *((double *)data); + data += sizeof(double); + return rv; +} + +static inline void *popPtr(const char *&data) +{ + void *rv = *((void **)data); + data += sizeof(void *); + return rv; +} + +// XXX TODO: Check that worker script is exception safe in the case of +// serialization/deserialization failures + +#define ALIGN(size) (((size) + 3) & ~3) +void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine) +{ + if (v.IsEmpty()) { + } else if (v->IsUndefined()) { + push(data, valueheader(WorkerUndefined)); + } else if (v->IsNull()) { + push(data, valueheader(WorkerNull)); + } else if (v->IsTrue()) { + push(data, valueheader(WorkerTrue)); + } else if (v->IsFalse()) { + push(data, valueheader(WorkerFalse)); + } else if (v->IsString()) { + v8::Handle<v8::String> string = v->ToString(); + int length = string->Length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(WorkerString, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + string->Write((uint16_t*)buffer); + } else if (v->IsFunction()) { + // XXX TODO: Implement passing function objects between the main and + // worker scripts + push(data, valueheader(WorkerUndefined)); + } else if (v->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(v); + uint32_t length = array->Length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerArray, length)); + for (uint32_t ii = 0; ii < length; ++ii) + serialize(data, array->Get(ii), engine); + } else if (v->IsInt32()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerInt32)); + push(data, (quint32)v->Int32Value()); + } else if (v->IsUint32()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerUint32)); + push(data, v->Uint32Value()); + } else if (v->IsNumber()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerNumber)); + push(data, v->NumberValue()); + } else if (v->IsDate()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerDate)); + push(data, v8::Handle<v8::Date>::Cast(v)->NumberValue()); + } else if (v->IsRegExp()) { + v8::Handle<v8::RegExp> regexp = v8::Handle<v8::RegExp>::Cast(v); + quint32 flags = regexp->GetFlags(); + v8::Local<v8::String> source = regexp->GetSource(); + + int length = source->Length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, sizeof(quint32) + utf16size); + push(data, valueheader(WorkerRegexp, flags)); + push(data, (quint32)length); + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + source->Write((uint16_t*)buffer); + } else if (v->IsObject() && !v->ToObject()->GetExternalResource()) { + v8::Handle<v8::Object> object = v->ToObject(); + v8::Local<v8::Array> properties = engine->getOwnPropertyNames(object); + quint32 length = properties->Length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + push(data, valueheader(WorkerObject, length)); + v8::TryCatch tc; + for (quint32 ii = 0; ii < length; ++ii) { + v8::Local<v8::String> str = properties->Get(ii)->ToString(); + serialize(data, str, engine); + + v8::Local<v8::Value> val = object->Get(str); + if (tc.HasCaught()) { + serialize(data, v8::Undefined(), engine); + tc.Reset(); + } else { + serialize(data, val, engine); + } + } + } else if (engine->isQObject(v)) { + // XXX TODO: Generalize passing objects between the main thread and worker scripts so + // that others can trivially plug in their elements. + QQuickListModel *lm = qobject_cast<QQuickListModel *>(engine->toQObject(v)); + if (lm && lm->agent()) { + QQuickListModelWorkerAgent *agent = lm->agent(); + agent->addref(); + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); + } else { + // we can convert sequences, but not other types with external data. + if (v->IsObject()) { + v8::Handle<v8::Object> seqObj = v->ToObject(); + QV8ObjectResource *r = static_cast<QV8ObjectResource *>(seqObj->GetExternalResource()); + if (r->resourceType() == QV8ObjectResource::SequenceType) { + QVariant sequenceVariant = engine->sequenceWrapper()->toVariant(r); + if (!sequenceVariant.isNull()) { + // valid sequence. we generate a length (sequence length + 1 for the sequence type) + uint32_t seqLength = engine->sequenceWrapper()->sequenceLength(r); + uint32_t length = seqLength + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerSequence, length)); + serialize(data, v8::Integer::New(sequenceVariant.userType()), engine); // sequence type + for (uint32_t ii = 0; ii < seqLength; ++ii) { + serialize(data, seqObj->Get(ii), engine); // sequence elements + } + + return; + } + } + } + + // not a sequence. + push(data, valueheader(WorkerUndefined)); + } +} + +v8::Handle<v8::Value> QV8Worker::deserialize(const char *&data, QV8Engine *engine) +{ + quint32 header = popUint32(data); + Type type = headertype(header); + + switch (type) { + case WorkerUndefined: + return v8::Undefined(); + case WorkerNull: + return v8::Null(); + case WorkerTrue: + return v8::True(); + case WorkerFalse: + return v8::False(); + case WorkerString: + { + quint32 size = headersize(header); + v8::Local<v8::String> string = v8::String::New((uint16_t*)data, size - 1); + data += ALIGN(size * sizeof(uint16_t)); + return string; + } + case WorkerFunction: + Q_ASSERT(!"Unreachable"); + break; + case WorkerArray: + { + quint32 size = headersize(header); + v8::Local<v8::Array> array = v8::Array::New(size); + for (quint32 ii = 0; ii < size; ++ii) { + array->Set(ii, deserialize(data, engine)); + } + return array; + } + case WorkerObject: + { + quint32 size = headersize(header); + v8::Local<v8::Object> o = v8::Object::New(); + for (quint32 ii = 0; ii < size; ++ii) { + v8::Handle<v8::Value> name = deserialize(data, engine); + v8::Handle<v8::Value> value = deserialize(data, engine); + o->Set(name, value); + } + return o; + } + case WorkerInt32: + return v8::Integer::New((qint32)popUint32(data)); + case WorkerUint32: + return v8::Integer::NewFromUnsigned(popUint32(data)); + case WorkerNumber: + return v8::Number::New(popDouble(data)); + case WorkerDate: + return v8::Date::New(popDouble(data)); + case WorkerRegexp: + { + quint32 flags = headersize(header); + quint32 length = popUint32(data); + v8::Local<v8::String> source = v8::String::New((uint16_t*)data, length - 1); + data += ALIGN(length * sizeof(uint16_t)); + return v8::RegExp::New(source, (v8::RegExp::Flags)flags); + } + case WorkerListModel: + { + void *ptr = popPtr(data); + QQuickListModelWorkerAgent *agent = (QQuickListModelWorkerAgent *)ptr; + v8::Handle<v8::Value> rv = engine->newQObject(agent); + if (rv->IsObject()) { + QQuickListModelWorkerAgent::VariantRef ref(agent); + QVariant var = qVariantFromValue(ref); + rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var)); + } + agent->release(); + agent->setV8Engine(engine); + return rv; + } + case WorkerSequence: + { + bool succeeded = false; + quint32 length = headersize(header); + quint32 seqLength = length - 1; + int sequenceType = deserialize(data, engine)->Int32Value(); + v8::Local<v8::Array> array = v8::Array::New(seqLength); + for (quint32 ii = 0; ii < seqLength; ++ii) + array->Set(ii, deserialize(data, engine)); + QVariant seqVariant = engine->sequenceWrapper()->toVariant(array, sequenceType, &succeeded); + return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded); + } + } + Q_ASSERT(!"Unreachable"); + return v8::Undefined(); +} + +QByteArray QV8Worker::serialize(v8::Handle<v8::Value> value, QV8Engine *engine) +{ + QByteArray rv; + serialize(rv, value, engine); + return rv; +} + +v8::Handle<v8::Value> QV8Worker::deserialize(const QByteArray &data, QV8Engine *engine) +{ + const char *stream = data.constData(); + return deserialize(stream, engine); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8worker_p.h b/src/qml/qml/v8/qv8worker_p.h new file mode 100644 index 0000000000..d398d21f60 --- /dev/null +++ b/src/qml/qml/v8/qv8worker_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8WORKER_P_H +#define QV8WORKER_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 "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +class QV8Worker { +public: + struct SavedData { + }; + + static QByteArray serialize(v8::Handle<v8::Value>, QV8Engine *); + static v8::Handle<v8::Value> deserialize(const QByteArray &, QV8Engine *); + +private: + static void serialize(QByteArray &, v8::Handle<v8::Value>, QV8Engine *); + static v8::Handle<v8::Value> deserialize(const char *&, QV8Engine *); +}; + +QT_END_NAMESPACE + +#endif // QV8WORKER_P_H diff --git a/src/qml/qml/v8/script.pri b/src/qml/qml/v8/script.pri new file mode 100644 index 0000000000..3439413f5e --- /dev/null +++ b/src/qml/qml/v8/script.pri @@ -0,0 +1,21 @@ +SOURCES += \ + $$PWD/qjsengine.cpp \ + $$PWD/qjsvalue.cpp \ + $$PWD/qjsvalueiterator.cpp \ + +HEADERS += \ + $$PWD/qjsengine.h \ + $$PWD/qjsengine_p.h \ + $$PWD/qjsvalue.h \ + $$PWD/qjsvalue_p.h \ + $$PWD/qjsvalueiterator.h \ + $$PWD/qjsvalue_impl_p.h \ + $$PWD/qjsconverter_p.h \ + $$PWD/qjsconverter_impl_p.h \ + $$PWD/qscriptisolate_p.h \ + $$PWD/qscriptshareddata_p.h \ + $$PWD/qscripttools_p.h \ + $$PWD/qscript_impl_p.h \ + $$PWD/qscriptoriginalglobalobject_p.h \ + $$PWD/qjsvalueiterator_p.h \ + $$PWD/qjsvalueiterator_impl_p.h diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri new file mode 100644 index 0000000000..de492a8ce5 --- /dev/null +++ b/src/qml/qml/v8/v8.pri @@ -0,0 +1,45 @@ +INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore + +include(script.pri) + +HEADERS += \ + $$PWD/qv8_p.h \ + $$PWD/qv8debug_p.h \ + $$PWD/qv8profiler_p.h \ + $$PWD/qv8stringwrapper_p.h \ + $$PWD/qv8engine_p.h \ + $$PWD/qv8sequencewrapper_p.h \ + $$PWD/qv8sequencewrapper_p_p.h \ + $$PWD/qv8contextwrapper_p.h \ + $$PWD/qv8qobjectwrapper_p.h \ + $$PWD/qv8typewrapper_p.h \ + $$PWD/qv8listwrapper_p.h \ + $$PWD/qv8variantwrapper_p.h \ + $$PWD/qv8variantresource_p.h \ + $$PWD/qv8valuetypewrapper_p.h \ + $$PWD/qv8include_p.h \ + $$PWD/qv8worker_p.h \ + $$PWD/qv8bindings_p.h \ + $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ + $$PWD/qv8engine_impl_p.h \ + $$PWD/qv8domerrors_p.h \ + $$PWD/qv8sqlerrors_p.h \ + $$PWD/qqmlbuiltinfunctions_p.h + +SOURCES += \ + $$PWD/qv8stringwrapper.cpp \ + $$PWD/qv8engine.cpp \ + $$PWD/qv8sequencewrapper.cpp \ + $$PWD/qv8contextwrapper.cpp \ + $$PWD/qv8qobjectwrapper.cpp \ + $$PWD/qv8typewrapper.cpp \ + $$PWD/qv8listwrapper.cpp \ + $$PWD/qv8variantwrapper.cpp \ + $$PWD/qv8valuetypewrapper.cpp \ + $$PWD/qv8include.cpp \ + $$PWD/qv8worker.cpp \ + $$PWD/qv8bindings.cpp \ + $$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \ + $$PWD/qv8domerrors.cpp \ + $$PWD/qv8sqlerrors.cpp \ + $$PWD/qqmlbuiltinfunctions.cpp
\ No newline at end of file |