diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2011-05-11 17:20:40 +1000 |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2011-06-06 11:50:48 +1000 |
commit | 6b54de600ce74025bc8ada20bea95ad183a6cd8d (patch) | |
tree | 6736888525cd8cd8c2d30bb7b87b3249b74839a5 /src/declarative/qml/v8 | |
parent | 6dbd4286eb19e9ac45665046a43342bcdc8b127e (diff) |
Initial V8 integration
Diffstat (limited to 'src/declarative/qml/v8')
25 files changed, 7025 insertions, 0 deletions
diff --git a/src/declarative/qml/v8/notes.txt b/src/declarative/qml/v8/notes.txt new file mode 100644 index 0000000000..ff5a289b7c --- /dev/null +++ b/src/declarative/qml/v8/notes.txt @@ -0,0 +1,4 @@ +Removed backwards compatible imports - QTBUG-17518 + +autotest print() taking objects that don't ToString() +autotest QDeclarativeV8Function diff --git a/src/declarative/qml/v8/qhashedstring.cpp b/src/declarative/qml/v8/qhashedstring.cpp new file mode 100644 index 0000000000..4a23e3b7dd --- /dev/null +++ b/src/declarative/qml/v8/qhashedstring.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhashedstring_p.h" + +inline unsigned stringHash(const QChar* data, unsigned length) +{ + return v8::String::ComputeHash((uint16_t *)data, length); +} + +void QHashedString::computeHash() const +{ + m_hash = stringHash(constData(), length()); +} + +void QHashedStringRef::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::rehash() +{ + numBits = qMax(MinNumBits, numBits + 1); + numBuckets = primeForNumBits(numBits); + + delete [] buckets; + buckets = new QStringHashNode *[numBuckets]; + ::memset(buckets, 0, sizeof(QStringHashNode *) * numBuckets); + + QStringHashNode *nodeList = nodes; + while (nodeList) { + int bucket = nodeList->key.hash() % numBuckets; + nodeList->next = buckets[bucket]; + buckets[bucket] = nodeList; + + nodeList = nodeList->nlist; + } +} + diff --git a/src/declarative/qml/v8/qhashedstring_p.h b/src/declarative/qml/v8/qhashedstring_p.h new file mode 100644 index 0000000000..acee03a9b5 --- /dev/null +++ b/src/declarative/qml/v8/qhashedstring_p.h @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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> + +QT_BEGIN_NAMESPACE + +class QHashedStringRef; +class 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 &); +private: + friend class QHashedStringRef; + + void computeHash() const; + mutable quint32 m_hash; +}; + +class QHashedStringRef +{ +public: + inline QHashedStringRef(); + inline QHashedStringRef(const QString &); + inline QHashedStringRef(const QChar *, int); + inline QHashedStringRef(const QChar *, int, quint32); + inline QHashedStringRef(const QHashedString &); + inline QHashedStringRef(const QHashedStringRef &); + + inline bool operator==(const QHashedString &string) const; + inline bool operator==(const QHashedStringRef &string) const; + + inline quint32 hash() const; + + inline const QChar *constData() const; + inline quint32 length() const; + inline bool startsWithUpper() const; + +private: + friend class QHashedString; + + void computeHash() const; + + const QChar *m_data; + quint32 m_length; + mutable quint32 m_hash; +}; + +class QStringHashData; +class QStringHashNode +{ +public: + QStringHashNode(const QHashedString &key) + : nlist(0), next(0), key(key) {} + + QStringHashNode *nlist; + QStringHashNode *next; + QHashedString key; +}; + +struct QStringHashData +{ +public: + QStringHashData() + : nodes(0), buckets(0), numBuckets(0), size(0), numBits(0) {} + + QStringHashNode *nodes; + QStringHashNode **buckets; + int numBuckets; + int size; + short numBits; + + void rehash(); +}; + +template<class T> +class QStringHash +{ +private: + struct Node : public QStringHashNode { + Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {} + T value; + }; + + QStringHashData data; + + inline Node *findNode(const QHashedStringRef &) const; + inline Node *findNode(v8::Handle<v8::String> &, quint32) const; + Node *createNode(const QHashedString &, const T &); + +public: + inline QStringHash(); + inline QStringHash(const QStringHash &); + inline ~QStringHash(); + + QStringHash &operator=(const QStringHash<T> &); + + inline bool isEmpty() const; + inline void clear(); + inline int count() const; + + inline void insert(const QString &, const T &); + inline void insert(const QHashedString &, const T &); + inline void insert(const QHashedStringRef &, const T &); + + inline T *value(const QString &) const; + inline T *value(const QHashedString &) const; + inline T *value(const QHashedStringRef &) const; + inline T *value(v8::Handle<v8::String> &) const; + inline T *value(v8::Handle<v8::String> &, quint32 hash) const; + + inline bool contains(const QString &) const; + inline bool contains(const QHashedString &) const; + inline bool contains(const QHashedStringRef &) const; + + T &operator[](const QString &); + T &operator[](const QHashedString &); + T &operator[](const QHashedStringRef &); + + class ConstIterator { + public: + ConstIterator() : n(0) {} + ConstIterator(Node *n) : n(n) {} + + ConstIterator &operator++() { n = (Node *)n->nlist; return *this; } + bool operator==(const ConstIterator &o) const { return n == o.n; } + bool operator!=(const ConstIterator &o) const { return n != o.n; } + + const QHashedString &key() const { return n->key; } + const T &value() const { return n->value; } + const T &operator*() const { return n->value; } + private: + Node *n; + }; + + ConstIterator begin() const { return ConstIterator((Node *)data.nodes); } + ConstIterator end() const { return ConstIterator(); } +}; + +template<class T> +QStringHash<T>::QStringHash() +{ +} + +template<class T> +QStringHash<T>::QStringHash(const QStringHash<T> &other) +: data(other.data) +{ + data.nodes = 0; + data.buckets = 0; + + QStringHashNode *n = other.data.nodes; + while (n) { + Node *o = (Node *)n; + Node *mynode = new Node(o->key, o->value); + mynode->nlist = data.nodes; + data.nodes = mynode; + n = o->nlist; + } + + data.rehash(); +} + +template<class T> +QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other) +{ + if (&other == this) + return *this; + + clear(); + data = other.data; + data.nodes = 0; + data.buckets = 0; + + QStringHashNode *n = other.data.nodes; + while (n) { + Node *o = (Node *)n; + Node *mynode = new Node(o->key, o->value); + mynode->nlist = data.nodes; + data.nodes = mynode; + n = o->nlist; + } + + data.rehash(); + + return *this; +} + +template<class T> +QStringHash<T>::~QStringHash() +{ + clear(); +} + +template<class T> +void QStringHash<T>::clear() +{ + QStringHashNode *n = data.nodes; + while (n) { + Node *o = (Node *)n; + n = n->nlist; + delete o; + } + + delete [] data.buckets; + + data = QStringHashData(); +} + +template<class T> +bool QStringHash<T>::isEmpty() const +{ + return data.nodes == 0; +} + +template<class T> +int QStringHash<T>::count() const +{ + return data.size; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const QHashedString &key, const T &value) +{ + if (data.size == data.numBuckets) + data.rehash(); + + Node *n = new Node(key, value); + n->nlist = data.nodes; + data.nodes = 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); + Node *n = 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) +{ + Node *n = findNode(key); + if (n) n->value = value; + else createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const QHashedStringRef &key, const T &value) +{ + Node *n = findNode(key); + if (n) n->value = value; + else createNode(key, value); +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const QHashedStringRef &string) const +{ + QStringHashNode *node = 0; + if (data.numBuckets) { + node = data.buckets[string.hash() % data.numBuckets]; + while (node && !(node->key == string)) + node = node->next; + } + + return (Node *)node; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::findNode(v8::Handle<v8::String> &string, quint32 hash) const +{ + QStringHashNode *node = 0; + if (data.numBuckets) { + node = data.buckets[hash % data.numBuckets]; + int length = string->Length(); + while (node && (length != node->key.length() || !string->Equals((uint16_t*)node->key.constData(), length))) + node = node->next; + } + + 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(v8::Handle<v8::String> &key) const +{ + return value(key, (quint32)key->Hash()); +} + +template<class T> +T *QStringHash<T>::value(v8::Handle<v8::String> &key, quint32 hash) const +{ + Node *n = findNode(key, hash); + 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> +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)->value; +} + +template<class T> +T &QStringHash<T>::operator[](const QHashedStringRef &key) +{ + Node *n = findNode(key); + if (n) return n->value; + else return createNode(key)->value; +} + +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 (uint)length() == string.m_length && + (string.m_hash == m_hash || !string.m_hash || !m_hash) && + 0 == ::memcmp(constData(), string.m_data, string.m_length * sizeof(QChar)); +} + +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)); +} + +QHashedStringRef::QHashedStringRef() +: m_data(0), m_length(0), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QString &str) +: m_data(str.constData()), m_length(str.length()), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QChar *data, int length) +: m_data(data), m_length(length), m_hash(0) +{ +} + +QHashedStringRef::QHashedStringRef(const QChar *data, int length, quint32 hash) +: m_data(data), m_length(length), m_hash(hash) +{ +} + +QHashedStringRef::QHashedStringRef(const QHashedString &string) +: m_data(string.constData()), m_length(string.length()), m_hash(string.m_hash) +{ +} + +QHashedStringRef::QHashedStringRef(const QHashedStringRef &string) +: m_data(string.m_data), m_length(string.m_length), m_hash(string.m_hash) +{ +} + +bool QHashedStringRef::operator==(const QHashedString &string) const +{ + return m_length == (uint)string.length() && + (m_hash == string.m_hash || !m_hash || !string.m_hash) && + 0 == ::memcmp(string.constData(), m_data, m_length * sizeof(QChar)); +} + +bool QHashedStringRef::operator==(const QHashedStringRef &string) const +{ + return m_length == string.m_length && + (m_hash == string.m_hash || !m_hash || !string.m_hash) && + 0 == ::memcmp(string.m_data, m_data, m_length * sizeof(QChar)); +} + +const QChar *QHashedStringRef::constData() const +{ + return m_data; +} + +quint32 QHashedStringRef::length() const +{ + return m_length; +} + +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; +} + +QT_END_NAMESPACE + +#endif // QHASHEDSTRING_P_H diff --git a/src/declarative/qml/v8/qv8_p.h b/src/declarative/qml/v8/qv8_p.h new file mode 100644 index 0000000000..6aeb6f6458 --- /dev/null +++ b/src/declarative/qml/v8/qv8_p.h @@ -0,0 +1 @@ +#include "../../../3rdparty/v8/include/v8.h" diff --git a/src/declarative/qml/v8/qv8contextwrapper.cpp b/src/declarative/qml/v8/qv8contextwrapper.cpp new file mode 100644 index 0000000000..1af4b4c883 --- /dev/null +++ b/src/declarative/qml/v8/qv8contextwrapper.cpp @@ -0,0 +1,425 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativecontext_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, QDeclarativeContextData *context, QObject *scopeObject); + ~QV8ContextResource(); + + inline QDeclarativeContextData *getContext() const; + + QDeclarativeGuard<QObject> scopeObject; + + quint32 hasSubContexts:1; + quint32 ownsContext:1; + quint32 readOnly:1; + quint32 dummy:29; + + QObject *secondaryScope; + + // XXX aakenned - this is somewhat of a horrible abuse of external strings :) + struct SubContext : public v8::String::ExternalStringResource { + SubContext(QDeclarativeContextData *context) : context(context) {} + QDeclarativeGuardedContextData context; + + virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); } + virtual size_t length() const { return internal.length(); } + }; + +private: + QDeclarativeGuardedContextData context; +}; + +QV8ContextResource::QV8ContextResource(QV8Engine *engine, QDeclarativeContextData *context, QObject *scopeObject) +: QV8ObjectResource(engine), scopeObject(scopeObject), hasSubContexts(false), ownsContext(false), + readOnly(true), secondaryScope(0), context(context) +{ +} + +QV8ContextResource::~QV8ContextResource() +{ + if (ownsContext && context) + context->destroy(); +} + +// Returns the context, including resolving a subcontext +QDeclarativeContextData *QV8ContextResource::getContext() const +{ + 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() +{ + m_urlConstructor.Dispose(); m_urlConstructor.Clear(); + m_constructor.Dispose(); m_constructor.Clear(); +} + +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 = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter); + m_urlConstructor = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } +} + +v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QDeclarativeContextData *ctxt, QObject *scope) +{ + // XXX aakenned - NewInstance() is slow for our case + 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) +{ + QDeclarativeContextData *context = new QDeclarativeContextData; + context->url = url; + context->isInternal = true; + context->isJSContext = true; + + // XXX aakenned - NewInstance() is slow for our case + v8::Local<v8::Object> rv = m_urlConstructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0); + r->ownsContext = true; + 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, + QDeclarativeContextData *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; +} + +QDeclarativeContextData *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; +} + +QDeclarativeContextData *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_cast<QV8ContextResource>(info.This()); + + if (!resource) + return v8::Undefined(); // XXX Should we throw here? + + 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_cast<QV8ContextResource>(info.This()); + + if (!resource) + return v8::Undefined(); // XXX Should we throw here? + + // XXX aakenned too agressive + QDeclarativeContextData *context = resource->getContext(); + + if (!context) + return v8::Undefined(); // XXX Should we throw here? + + // 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->scopeObject; + + if (context->imports && QV8Engine::startsWithUpper(property)) { + // Search for attached properties, enums and imported scripts + QDeclarativeTypeNameCache::Data *data = context->imports->data(property); + + if (data) { + if (data->importedScriptIndex != -1) { + int index = data->importedScriptIndex; + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + else + return v8::Undefined(); + } else if (data->type) { + return engine->typeWrapper()->newObject(scopeObject, data->type); + } else if (data->typeNamespace) { + return engine->typeWrapper()->newObject(scopeObject, data->typeNamespace); + } + Q_ASSERT(!"Unreachable"); + } + + // Fall through + } + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + if (resource->secondaryScope) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, property, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) return result; + } + + while (context) { + // Search context properties + if (context->propertyNames) { + int propertyIdx = context->propertyNames->value(property); + + if (propertyIdx != -1) { + typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; + + if (propertyIdx < context->idValueCount) { + + if (ep->captureProperties) + ep->capturedProperties << CapturedProperty(&context->idValues[propertyIdx].bindings); + + return engine->newQObject(context->idValues[propertyIdx]); + } else { + + QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate(); + + if (ep->captureProperties) + ep->capturedProperties << CapturedProperty(context->asQDeclarativeContext(), -1, + propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (value.userType() == qMetaTypeId<QList<QObject*> >()) { + QDeclarativeListProperty<QObject> prop(context->asQDeclarativeContext(), (void*)propertyIdx, + 0, + QDeclarativeContextPrivate::context_count, + QDeclarativeContextPrivate::context_at); + return engine->listWrapper()->newList(prop, qMetaTypeId<QDeclarativeListProperty<QObject> >()); + } else { + return engine->fromVariant(cp->propertyValues.at(propertyIdx)); + } + } + } + } + + // Search scope object + if (scopeObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, property, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + scopeObject = 0; + + + // Search context object + if (context->contextObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, property, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + + context = context->parent; + } + + 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_cast<QV8ContextResource>(info.This()); + + if (!resource) + return v8::Undefined(); // XXX Should we throw here? + + 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::Undefined(); + } +} + +v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(info.This()); + + if (!resource) + return v8::Undefined(); // XXX Should we throw here? + + // XXX aakenned too agressive + QDeclarativeContextData *context = resource->getContext(); + + if (!context) + return v8::Undefined(); // XXX Should we throw here? + + // See QV8ContextWrapper::Getter for resolution order + + QV8Engine *engine = resource->engine; + QObject *scopeObject = resource->scopeObject; + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + // Search scope object + if (resource->secondaryScope && qobjectWrapper->setProperty(resource->secondaryScope, property, value, + QV8QObjectWrapper::IgnoreRevision)) + return value; + + while (context) { + // Search context properties + if (context->propertyNames && -1 != context->propertyNames->value(property)) + return value; + + // Search scope object + if (scopeObject && + qobjectWrapper->setProperty(scopeObject, property, value, QV8QObjectWrapper::CheckRevision)) + return value; + scopeObject = 0; + + // Search context object + if (context->contextObject && + qobjectWrapper->setProperty(context->contextObject, property, value, QV8QObjectWrapper::CheckRevision)) + return value; + + context = context->parent; + } + + 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/declarative/qml/v8/qv8contextwrapper_p.h b/src/declarative/qml/v8/qv8contextwrapper_p.h new file mode 100644 index 0000000000..cc0705995b --- /dev/null +++ b/src/declarative/qml/v8/qv8contextwrapper_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 QDeclarativeContextData; +class QV8ContextWrapper +{ +public: + QV8ContextWrapper(); + ~QV8ContextWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> qmlScope(QDeclarativeContextData *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>, + QDeclarativeContextData *ctxt); + + // XXX aakenned - remove this abomination + QObject *setSecondaryScope(v8::Handle<v8::Object>, QObject *); + + QDeclarativeContextData *callingContext(); + QDeclarativeContextData *context(v8::Handle<v8::Value>); +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; +}; + +QT_END_NAMESPACE + +#endif // QV8CONTEXTWRAPPER_P_H + diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp new file mode 100644 index 0000000000..ced07e142f --- /dev/null +++ b/src/declarative/qml/v8/qv8engine.cpp @@ -0,0 +1,1423 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8engine_p.h" + +#include "qv8contextwrapper_p.h" +#include "qv8include_p.h" +#include "../../../3rdparty/javascriptcore/DateMath.h" + +#include <private/qdeclarativelist_p.h> +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativecomponent_p.h> +#include <private/qdeclarativestringconverters_p.h> + +#include <QtDeclarative/qdeclarativecomponent.h> + +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qnumeric.h> +#include <QtGui/qdesktopservices.h> +#include <QtGui/qfontdatabase.h> +#include <private/qdeclarativeapplication_p.h> +#include <private/qdeclarativexmlhttprequest_p.h> +#include <private/qdeclarativesqldatabase_p.h> + +// XXX Need to check all the global functions will also work in a worker script where the QDeclarativeEngine +// is not available +QT_BEGIN_NAMESPACE + +QV8Engine::QV8Engine() +: m_xmlHttpRequestData(0), m_sqlDatabaseData(0) +{ +} + +QV8Engine::~QV8Engine() +{ + qt_rem_qmlsqldatabase(this, m_sqlDatabaseData); + m_sqlDatabaseData = 0; + qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); + m_xmlHttpRequestData = 0; + + m_getOwnPropertyNames.Dispose(); m_getOwnPropertyNames.Clear(); + + m_valueTypeWrapper.destroy(); + m_variantWrapper.destroy(); + m_listWrapper.destroy(); + m_typeWrapper.destroy(); + m_qobjectWrapper.destroy(); + m_contextWrapper.destroy(); + m_stringWrapper.destroy(); + m_context.Dispose(); +} + +void QV8Engine::init(QDeclarativeEngine *engine) +{ + m_engine = engine; + + QByteArray v8args = qgetenv("V8ARGS"); + if (!v8args.isEmpty()) + v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); + + v8::HandleScope handle_scope; + m_context = v8::Context::New(); + v8::Context::Scope context_scope(m_context); + + 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); + + { + v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); + m_getOwnPropertyNames = v8::Persistent<v8::Function>::New(v8::Handle<v8::Function>::Cast(v)); + } + + initializeGlobal(m_context->Global()); + freezeGlobal(); +} + +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 (value->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); + if (r) { + switch (r->resourceType()) { + case QV8ObjectResource::ContextType: + case QV8ObjectResource::TypeType: + case QV8ObjectResource::XMLHttpRequestType: + case QV8ObjectResource::DOMNodeType: + case QV8ObjectResource::SQLDatabaseType: + return QVariant(); + 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); + } + } + } + + if (typeHint == qMetaTypeId<QList<QObject *> >() && value->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + + 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); + } + + 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); + +// 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 v8::Handle<v8::RegExp> regexpFromQRegExp(QV8Engine *engine, 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(engine->toString(pattern), static_cast<v8::RegExp::Flags>(flags)); +} + +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 regexpFromQRegExp(this, *reinterpret_cast<const QRegExp *>(ptr)); + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return newQObject(*reinterpret_cast<QObject* const *>(ptr)); + case QMetaType::QStringList: + 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 (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) + return m_valueTypeWrapper.newValueType(variant, vt); + + } else { + if (type == qMetaTypeId<QDeclarativeListReference>()) { + typedef QDeclarativeListReferencePrivate QDLRP; + QDLRP *p = QDLRP::get((QDeclarativeListReference*)ptr); + if (p->object) { + return m_listWrapper.newList(p->property, p->propertyType); + } else { + return v8::Null(); + } + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX aakenned Can this be more optimal? Just use Array as a prototype and + // implement 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 = QDeclarativeMetaType::toQObject(variant, &objOk); + if (objOk) + return newQObject(obj); + } + + return m_variantWrapper.newVariant(variant); + + // XXX aakenned +#if 0 +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + result = newRegExp(exec, *reinterpret_cast<const QRegExp *>(ptr)); + break; +#endif +#ifndef QT_NO_QOBJECT +#endif + case QMetaType::QVariant: + result = eng->newVariant(*reinterpret_cast<const QVariant*>(ptr)); + break; + default: + if (type == qMetaTypeId<QScriptValue>()) { + result = eng->scriptValueToJSCValue(*reinterpret_cast<const QScriptValue*>(ptr)); + if (!result) + return JSC::jsUndefined(); + } + +#ifndef QT_NO_QOBJECT + // lazy registration of some common list types + else if (type == qMetaTypeId<QObjectList>()) { + qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func()); + return create(exec, type, ptr); + } +#endif + else if (type == qMetaTypeId<QList<int> >()) { + qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); + return create(exec, type, ptr); + } + + else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr)) + return JSC::jsNull(); + else + result = eng->newVariant(QVariant(type, ptr)); + } + } + } +#endif +} + +// 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; +} + +QNetworkAccessManager *QV8Engine::networkAccessManager() +{ + return QDeclarativeEnginePrivate::get(m_engine)->getNetworkAccessManager(); +} + +const QSet<QString> &QV8Engine::illegalNames() const +{ + return m_illegalNames; +} + +// Requires a handle scope +v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o) +{ + 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); +} + +QDeclarativeContextData *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(); + else if (value->IsBoolean()) + return value->ToBoolean()->Value(); + else if (value->IsInt32()) + return value->ToInt32()->Value(); + else if (value->IsNumber()) + return value->ToNumber()->Value(); + else 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()); + v8::Handle<v8::RegExp> jsRegExp = v8::Handle<v8::RegExp>::Cast(value); + // Copied from QtScript + // 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. + QString pattern = toString(jsRegExp->GetSource()); + Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive; + if (jsRegExp->GetFlags() & v8::RegExp::kIgnoreCase) + caseSensitivity = Qt::CaseInsensitive; + return QRegExp(pattern, caseSensitivity, QRegExp::RegExp2); + } else 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; + } else if (!value->IsFunction()) { + v8::Context::Scope scope(context()); + v8::Handle<v8::Object> object = value->ToObject(); + v8::Local<v8::Array> properties = object->GetPropertyNames(); + int length = properties->Length(); + if (length == 0) + return QVariant(); + + QVariantMap map; + for (int ii = 0; ii < length; ++ii) { + v8::Handle<v8::Value> property = properties->Get(ii); + map.insert(toString(property), toVariant(object->Get(property), -1)); + } + return map; + } + + return QVariant(); +} + + + +#include <QtGui/qvector3d.h> + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) +{ + v8::Local<v8::Function> printFn = V8FUNCTION(print, this); + + v8::Local<v8::Object> console = v8::Object::New(); + console->Set(v8::String::New("log"), printFn); + console->Set(v8::String::New("debug"), printFn); + + // XXX - Qt global object properties + + 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))); + } + } + + if (m_engine) + qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); + + 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)); + + if (m_engine) { + 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("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("quit"), V8FUNCTION(quit, this)); + qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); + + if (m_engine) { + qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); + qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); + } + + // XXX translator functions + + global->Set(v8::String::New("print"), printFn); + global->Set(v8::String::New("console"), console); + global->Set(v8::String::New("Qt"), qt); + + // XXX mainthread only + m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); + m_sqlDatabaseData = qt_add_qmlsqldatabase(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))); + } +} + +void QV8Engine::freezeGlobal() +{ + // Freeze the global object + // XXX I don't think this is sufficient as it misses non-enumerable properties +#define FREEZE "(function freeze_recur(obj) { "\ + " if (Qt.isQtObject(obj)) return;"\ + " for (var prop in obj) { " \ + " if (prop == \"connect\" || prop == \"disconnect\") {" \ + " Object.freeze(obj[prop]); "\ + " continue;" \ + " }" \ + " freeze_recur(obj[prop]);" \ + " }" \ + " if (obj instanceof Object) {" \ + " Object.freeze(obj);" \ + " }"\ + "})(this);" + v8::Local<v8::Script> test = v8::Script::New(v8::String::New(FREEZE)); +#undef FREEZE + + test->Run(); +} + +v8::Handle<v8::Value> QV8Engine::print(const v8::Arguments &args) +{ + QString result; + for (int i = 0; i < args.Length(); ++i) { + if (i != 0) + result.append(QLatin1Char(' ')); + + v8::Local<v8::String> jsstr = args[i]->ToString(); + if (!jsstr.IsEmpty()) { + QString qstr; + qstr.resize(jsstr->Length()); + jsstr->Write((uint16_t*)qstr.data()); + result.append(qstr); + } + } + qDebug("%s", qPrintable(result)); + return v8::Undefined(); +} + +/*! +\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> QV8Engine::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> QV8Engine::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> QV8Engine::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> QV8Engine::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> QV8Engine::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> QV8Engine::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> QV8Engine::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 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> QV8Engine::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 = QDeclarativeStringConverters::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> QV8Engine::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 = QDeclarativeStringConverters::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> QV8Engine::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 = QDeclarativeStringConverters::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 = QDeclarativeStringConverters::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}. +*/ +v8::Handle<v8::Value> QV8Engine::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}. +*/ +v8::Handle<v8::Value> QV8Engine::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 +*/ +v8::Handle<v8::Value> QV8Engine::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)); +} + +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()); +} + +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(); +} + +/*! +\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> QV8Engine::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> QV8Engine::resolvedUrl(const v8::Arguments &args) +{ + QUrl url = V8ENGINE()->toVariant(args[0], -1).toUrl(); + // XXX uses QDeclarativeEngine which means it wont work in worker script? + QDeclarativeEngine *e = V8ENGINE()->engine(); + QDeclarativeEnginePrivate *p = 0; + if (e) p = QDeclarativeEnginePrivate::get(e); + if (p) { + QDeclarativeContextData *ctxt = V8ENGINE()->callingContext(); + if (ctxt) + return V8ENGINE()->fromVariant(ctxt->resolvedUrl(url)); + else + return V8ENGINE()->fromVariant(url); + } + + return V8ENGINE()->fromVariant(e->baseUrl().resolved(url)); +} + +/*! +\qmlmethod list<string> Qt::fontFamilies() +Returns a list of the font families available to the application. +*/ +v8::Handle<v8::Value> QV8Engine::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> QV8Engine::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> QV8Engine::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> QV8Engine::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 QDeclarativeEngine::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 +QDeclarativeEngine::quit() signal to the QCoreApplication::quit() slot. +*/ +v8::Handle<v8::Value> QV8Engine::quit(const v8::Arguments &args) +{ + // XXX worker script? + QDeclarativeEnginePrivate::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/declarative/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> QV8Engine::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<QDeclarativeError> &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 QDeclarativeError &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.line())); + 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(); + QDeclarativeEngine *engine = v8engine->engine(); + + QDeclarativeContextData *context = v8engine->callingContext(); + Q_ASSERT(context); + + 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"); + + QDeclarativeComponent 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(context->asQDeclarativeContext()); + if(obj) + QDeclarativeData::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<QDeclarativePrivate::AutoParentFunction> functions = QDeclarativeMetaType::parentFunctions(); + for (int ii = 0; ii < functions.count(); ++ii) { + if (QDeclarativePrivate::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/declarative/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> QV8Engine::createComponent(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.createComponent(): Invalid arguments"); + + QV8Engine *v8engine = V8ENGINE(); + QDeclarativeEngine *engine = v8engine->engine(); + + QDeclarativeContextData *context = v8engine->callingContext(); + Q_ASSERT(context); + + QString arg = v8engine->toString(args[0]->ToString()); + if (arg.isEmpty()) + return v8::Null(); + + QUrl url = context->resolvedUrl(QUrl(arg)); + QDeclarativeComponent *c = new QDeclarativeComponent(engine, url, engine); + QDeclarativeComponentPrivate::get(c)->creationContext = context; + QDeclarativeData::get(c, true)->setImplicitDestructible(); + return v8engine->newQObject(c); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h new file mode 100644 index 0000000000..d4b8730dc7 --- /dev/null +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV8ENGINE_P_H +#define QDECLARATIVEV8ENGINE_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 <private/qv8_p.h> + +#include <private/qdeclarativepropertycache_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" + +QT_BEGIN_NAMESPACE + +#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() +// XXX Are we mean to return a value here, or is an empty handle ok? +#define V8THROW_ERROR(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} + +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 }; + virtual ResourceType resourceType() const = 0; + + QV8Engine *engine; +}; + +template<class T> +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; +} + +// 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(QDeclarativeV8Function*); +// }; +// The QDeclarativeV8Function - 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 QDeclarativeV8Function +{ +public: + int Length() const { return _ac; } + v8::Local<v8::Value> operator[](int idx) { return (*_a)->Get(idx); } + QDeclarativeContextData *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; + QDeclarativeV8Function(); + QDeclarativeV8Function(const QDeclarativeV8Function &); + QDeclarativeV8Function &operator=(const QDeclarativeV8Function &); + + QDeclarativeV8Function(int length, v8::Handle<v8::Object> &args, + v8::Handle<v8::Value> &rv, v8::Handle<v8::Object> &global, + QDeclarativeContextData *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; + QDeclarativeContextData *_c; + QV8Engine *_e; +}; + +class QDeclarativeV8Handle +{ +public: + QDeclarativeV8Handle() : d(0) {} + QDeclarativeV8Handle(const QDeclarativeV8Handle &other) : d(other.d) {} + QDeclarativeV8Handle &operator=(const QDeclarativeV8Handle &other) { d = other.d; return *this; } + + static QDeclarativeV8Handle fromHandle(v8::Handle<v8::Value> h) { + return reinterpret_cast<QDeclarativeV8Handle &>(h); + } + v8::Handle<v8::Value> toHandle() const { + return reinterpret_cast<const v8::Handle<v8::Value> &>(*this); + } +private: + void *d; +}; + +class QObject; +class QDeclarativeEngine; +class QDeclarativeValueType; +class QNetworkAccessManager; +class QDeclarativeContextData; +class Q_AUTOTEST_EXPORT QV8Engine +{ +public: + QV8Engine(); + ~QV8Engine(); + + void init(QDeclarativeEngine *); + + QDeclarativeEngine *engine() { return m_engine; } + v8::Local<v8::Object> global() { return m_context->Global(); } + v8::Handle<v8::Context> context() { return m_context; } + 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; } + + void *xmlHttpRequestData() { return m_xmlHttpRequestData; } + void *sqlDatabaseData() { return m_sqlDatabaseData; } + + QDeclarativeContextData *callingContext(); + + v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>); + + 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); + + // Return the QML global "scope" object for the \a ctxt context and \a scope object. + inline v8::Local<v8::Object> qmlScope(QDeclarativeContextData *ctxt, QObject *scope); + + // Return a JS wrapper for the given QObject \a object + inline v8::Handle<v8::Value> newQObject(QObject *object); + 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, QDeclarativeValueType *); + inline v8::Handle<v8::Value> newValueType(const QVariant &, QDeclarativeValueType *); + + // 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 QDeclarativeEngine. 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 QSet<QString> &illegalNames() const; + +private: + QDeclarativeEngine *m_engine; + v8::Persistent<v8::Context> m_context; + + QV8StringWrapper m_stringWrapper; + QV8ContextWrapper m_contextWrapper; + QV8QObjectWrapper m_qobjectWrapper; + QV8TypeWrapper m_typeWrapper; + QV8ListWrapper m_listWrapper; + QV8VariantWrapper m_variantWrapper; + QV8ValueTypeWrapper m_valueTypeWrapper; + + v8::Persistent<v8::Function> m_getOwnPropertyNames; + + void *m_xmlHttpRequestData; + void *m_sqlDatabaseData; + + QSet<QString> m_illegalNames; + + QVariant toBasicVariant(v8::Handle<v8::Value>); + + void initializeGlobal(v8::Handle<v8::Object>); + void freezeGlobal(); + + static v8::Handle<v8::Value> print(const v8::Arguments &args); + static v8::Handle<v8::Value> isQtObject(const v8::Arguments &args); + static v8::Handle<v8::Value> rgba(const v8::Arguments &args); + static v8::Handle<v8::Value> hsla(const v8::Arguments &args); + static v8::Handle<v8::Value> rect(const v8::Arguments &args); + static v8::Handle<v8::Value> point(const v8::Arguments &args); + static v8::Handle<v8::Value> size(const v8::Arguments &args); + static v8::Handle<v8::Value> vector3d(const v8::Arguments &args); + static v8::Handle<v8::Value> lighter(const v8::Arguments &args); + static v8::Handle<v8::Value> darker(const v8::Arguments &args); + static v8::Handle<v8::Value> tint(const v8::Arguments &args); + static v8::Handle<v8::Value> formatDate(const v8::Arguments &args); + static v8::Handle<v8::Value> formatTime(const v8::Arguments &args); + static v8::Handle<v8::Value> formatDateTime(const v8::Arguments &args); + static v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args); + static v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args); + static v8::Handle<v8::Value> md5(const v8::Arguments &args); + static v8::Handle<v8::Value> btoa(const v8::Arguments &args); + static v8::Handle<v8::Value> atob(const v8::Arguments &args); + static v8::Handle<v8::Value> quit(const v8::Arguments &args); + static v8::Handle<v8::Value> resolvedUrl(const v8::Arguments &args); + static v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args); + static v8::Handle<v8::Value> createComponent(const v8::Arguments &args); + + double qtDateTimeToJsDate(const QDateTime &dt); + QDateTime qtDateTimeFromJsDate(double jsDate); +}; + +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(QDeclarativeContextData *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::Local<v8::String> QV8Engine::toString(const QString &string) +{ + return m_stringWrapper.toString(string); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(QObject *object, int property, QDeclarativeValueType *type) +{ + return m_valueTypeWrapper.newValueType(object, property, type); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(const QVariant &value, QDeclarativeValueType *type) +{ + return m_valueTypeWrapper.newValueType(value, type); +} + +// XXX perf? +bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string) +{ + uint16_t buffer[2]; + int written = string->Write(buffer, 0, 1); + if (written == 0) return false; + uint16_t c = buffer[0]; + return ((c != '_' ) && (!(c >= 'a' && c <= 'z')) && + ((c >= 'A' && c <= 'Z') || QChar::category(c) == QChar::Letter_Uppercase)); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVEV8ENGINE_P_H diff --git a/src/declarative/qml/v8/qv8include.cpp b/src/declarative/qml/v8/qv8include.cpp new file mode 100644 index 0000000000..f2b25e0a92 --- /dev/null +++ b/src/declarative/qml/v8/qv8include.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8include_p.h" + +#include <QtScript/qscriptengine.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qfile.h> + +#include <private/qdeclarativeengine_p.h> + +QT_BEGIN_NAMESPACE + +QV8Include::QV8Include(const QUrl &url, QV8Engine *engine, QDeclarativeContextData *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 = v8::Persistent<v8::Object>::New(qmlglobal); + if (!callback.IsEmpty()) + m_callbackFunction = v8::Persistent<v8::Function>::New(callback); + + m_resultObject = v8::Persistent<v8::Object>::New(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; + m_callbackFunction.Dispose(); + m_resultObject.Dispose(); +} + +v8::Local<v8::Object> QV8Include::resultValue(Status status) +{ + // XXX aakenned inefficient + 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 }; + // XXX TryCatch? + 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); + QDeclarativeScriptParser::extractPragmas(code); + + QDeclarativeContextData *importContext = new QDeclarativeContextData; + importContext->isInternal = true; + importContext->isJSContext = true; + importContext->url = m_url; + 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(); + QDeclarativeContextData *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 = QDeclarativeEnginePrivate::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); + QDeclarativeScriptParser::extractPragmas(code); + + QDeclarativeContextData *importContext = new QDeclarativeContextData; + 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/declarative/qml/v8/qv8include_p.h b/src/declarative/qml/v8/qv8include_p.h new file mode 100644 index 0000000000..76997ba649 --- /dev/null +++ b/src/declarative/qml/v8/qv8include_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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/qdeclarativecontext_p.h> +#include <private/qdeclarativeguard_p.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +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 *, QDeclarativeContextData *, + 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; + QDeclarativeGuard<QNetworkReply> m_reply; + + QUrl m_url; + int m_redirectCount; + + v8::Persistent<v8::Function> m_callbackFunction; + v8::Persistent<v8::Object> m_resultObject; + + QDeclarativeGuardedContextData m_context; + v8::Persistent<v8::Object> m_qmlglobal; +}; + +QT_END_NAMESPACE + +#endif // QV8INCLUDE_P_H + diff --git a/src/declarative/qml/v8/qv8listwrapper.cpp b/src/declarative/qml/v8/qv8listwrapper.cpp new file mode 100644 index 0000000000..9e1110fd85 --- /dev/null +++ b/src/declarative/qml/v8/qv8listwrapper.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8listwrapper_p.h" +#include "qv8engine_p.h" +#include <private/qdeclarativelist_p.h> + +QT_BEGIN_NAMESPACE + +class QV8ListResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ListType); +public: + QV8ListResource(QV8Engine *engine) : QV8ObjectResource(engine) {} + + QDeclarativeGuard<QObject> object; + QDeclarativeListProperty<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); + 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)); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = v8::Persistent<v8::Function>::New(ft->GetFunction()); +} + +void QV8ListWrapper::destroy() +{ + m_constructor.Dispose(); +} + +v8::Handle<v8::Value> QV8ListWrapper::newList(QObject *object, int propId, int propType) +{ + if (!object || propId == -1) + return v8::Null(); + + // XXX aakenned - NewInstance() is slow for our case + 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 QDeclarativeListProperty<QObject> &prop, int propType) +{ + // XXX aakenned - NewInstance() is slow for our case + 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(QDeclarativeListReferencePrivate::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::Undefined(); +} + +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); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8listwrapper_p.h b/src/declarative/qml/v8/qv8listwrapper_p.h new file mode 100644 index 0000000000..82fdaa7271 --- /dev/null +++ b/src/declarative/qml/v8/qv8listwrapper_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 <QtDeclarative/qdeclarativelist.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 QDeclarativeListProperty<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); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8LISTWRAPPER_P_H + diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp new file mode 100644 index 0000000000..99cede2072 --- /dev/null +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -0,0 +1,1714 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8qobjectwrapper_p.h" +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qdeclarativeguard_p.h> +#include <private/qdeclarativepropertycache_p.h> +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativevmemetaobject_p.h> +#include <private/qdeclarativebinding_p.h> + +#include <QtScript/qscriptvalue.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qtimer.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QDeclarativeV8Handle); + +#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 + +class QV8QObjectResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(QObjectType); + +public: + QV8QObjectResource(QV8Engine *engine, QObject *object); + + QDeclarativeGuard<QObject> object; +}; + +namespace { +struct MetaCallArgument { + inline MetaCallArgument(); + inline ~MetaCallArgument(); + 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: + MetaCallArgument(const MetaCallArgument &); + + inline void cleanup(); + + char data[4 * sizeof(void *)]; + int type; + bool isObjectType; +}; +} + +QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) +: QV8ObjectResource(engine), object(object) +{ +} + +QV8QObjectWrapper::QV8QObjectWrapper() +: m_engine(0) +{ +} + +QV8QObjectWrapper::~QV8QObjectWrapper() +{ +} + +void QV8QObjectWrapper::destroy() +{ + qDeleteAll(m_connections); + m_connections.clear(); + + m_hiddenObject.Dispose(); + m_destroySymbol.Dispose(); + m_toStringSymbol.Dispose(); + m_methodConstructor.Dispose(); + m_constructor.Dispose(); +} + +#define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \ +static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \ +{ \ + v8::Handle<v8::Object> This = info.This(); \ + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(This); \ + \ + if (!resource || resource->object.isNull()) return v8::Undefined(); \ + \ + QObject *object = resource->object; \ + \ + uint32_t data = info.Data()->Uint32Value(); \ + int index = data & 0x7FFF; \ + int notify = (data & 0x7FFF0000) >> 16; \ + if (notify == 0x7FFF) notify = -1; \ + \ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); \ + if (notify /* 0 means constant */ && ep->captureProperties) { \ + typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \ + ep->capturedProperties << CapturedProperty(object, index, notify); \ + } \ + \ + cpptype value = defaultvalue; \ + void *args[] = { &value, 0 }; \ + QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \ + \ + return constructor(value); \ +} + +#define CREATE_FUNCTION \ + "(function(method) { "\ + "return (function(object, data, qmlglobal) { "\ + "return (function() { "\ + "return method(object, data, qmlglobal, arguments.length, arguments); "\ + "});"\ + "});"\ + "})" + +void QV8QObjectWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + + m_toStringSymbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("toString")); + m_destroySymbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("destroy")); + m_hiddenObject = v8::Persistent<v8::Object>::New(v8::Object::New()); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } + { + v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions + v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin); + 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 = v8::Persistent<v8::Function>::New(createFn); + } + + { + 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"), V8FUNCTION(Connect, engine)); + prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine)); + } +} + +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::QV8QObjectWrapper::toQObject(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType); + return static_cast<QV8QObjectResource *>(r)->object; +} + +// Load value properties +static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, + const QDeclarativePropertyCache::Data &property) +{ + Q_ASSERT(!property.isFunction()); + +#define PROPERTY_LOAD(metatype, cpptype, constructor) \ + if (property.propType == QMetaType:: metatype) { \ + cpptype type = cpptype(); \ + void *args[] = { &type, 0 }; \ + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \ + return constructor(type); \ + } + + if (property.isQObject()) { + QObject *rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + return engine->newQObject(rv); + } else if (property.isQList()) { + return engine->listWrapper()->newList(object, property.coreIndex, property.propType); + } else PROPERTY_LOAD(QReal, qreal, v8::Number::New) + else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Number::New) + else PROPERTY_LOAD(Bool, bool, v8::Boolean::New) + else PROPERTY_LOAD(QString, QString, engine->toString) + else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned) + else PROPERTY_LOAD(Float, float, v8::Number::New) + else PROPERTY_LOAD(Double, double, v8::Number::New) + else if(property.isV8Handle()) { + QDeclarativeV8Handle handle; + void *args[] = { &handle, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + return reinterpret_cast<v8::Handle<v8::Value> &>(handle); + } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + QDeclarativeValueType *valueType = ep->valueTypes[property.propType]; + if (valueType) + return engine->newValueType(object, property.coreIndex, valueType); + } + + QVariant var = object->metaObject()->property(property.coreIndex).read(object); + return engine->fromVariant(var); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + v8::Handle<v8::String> property, + QV8QObjectWrapper::RevisionMode revisionMode) +{ + // XXX aakenned This can't possibly be the best solution!!! + 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); + } + }; + + if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX); + } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX); + } + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *result = 0; + result = QDeclarativePropertyCache::property(engine->engine(), object, property, local); + + if (!result) + return v8::Handle<v8::Value>(); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QDeclarativeData *ddata = QDeclarativeData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return v8::Handle<v8::Value>(); + } + + typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; + + if (result->isFunction()) { + if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) { + return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex); + } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) { + return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex); + } else { + return MethodClosure::create(engine, object, objectHandle, result->coreIndex); + } + } + + if (ep->captureProperties && !result->isConstant()) { + if (result->coreIndex == 0) + ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier()); + else + ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex); + } + + return LoadProperty(engine, object, *result); +} + +// Setter for writable properties. Shared between the interceptor and fast property accessor +static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property, + v8::Handle<v8::Value> value) +{ + QDeclarativeBinding *newBinding = 0; + + if (value->IsFunction()) { + QDeclarativeContextData *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(); + QString url = engine->toString(frame->GetScriptName()); + + QDeclarativePropertyCache::ValueTypeData valueTypeData; + newBinding = new QDeclarativeBinding(&function, object, context); + newBinding->setSourceLocation(url, lineNumber); + newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context)); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject); + } + + QDeclarativeAbstractBinding *oldBinding = + QDeclarativePropertyPrivate::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 { + QVariant v; + if (property->isQList()) + v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + else + v = engine->toVariant(value, property->propType); + + QDeclarativeContextData *context = engine->callingContext(); + + if (!QDeclarativePropertyPrivate::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, v8::Handle<v8::String> property, + v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode) +{ + if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) || + engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) + return true; + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *result = 0; + result = QDeclarativePropertyCache::property(engine->engine(), object, property, local); + + if (!result) + return false; + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QDeclarativeData *ddata = QDeclarativeData::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) + 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_cast<QV8QObjectResource>(info.This()); + v8::Handle<v8::Value> This = info.This(); + + if (!resource || resource->object.isNull()) return v8::Undefined(); + + QObject *object = resource->object; + + QV8Engine *v8engine = resource->engine; + v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) + return result; + + if (QV8Engine::startsWithUpper(property)) { + // Check for attached properties + QDeclarativeContextData *context = v8engine->callingContext(); + QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0; + + if (data) { + if (data->type) { + return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums); + } else if (data->typeNamespace) { + return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums); + } + } + + return v8::Undefined(); + } else { + // XXX throw? + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This()); + + if (!resource || resource->object.isNull()) + return value; + + QObject *object = resource->object; + + QV8Engine *v8engine = resource->engine; + bool result = SetProperty(v8engine, object, property, 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_cast<QV8QObjectResource>(info.This()); + + if (!resource || resource->object.isNull()) + return v8::Handle<v8::Integer>(); + + QV8Engine *engine = resource->engine; + QObject *object = resource->object; + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *result = 0; + result = QDeclarativePropertyCache::property(engine->engine(), object, property, 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_cast<QV8QObjectResource>(info.This()); + + if (!resource || resource->object.isNull()) + return v8::Array::New(); + + QObject *object = resource->object; + + QStringList result; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); + + QDeclarativePropertyCache *cache = 0; + QDeclarativeData *ddata = QDeclarativeData::get(object); + if (ddata) + cache = ddata->propertyCache; + + if (!cache) { + cache = ep->cache(object); + if (cache) { + if (ddata) { cache->addref(); ddata->propertyCache = cache; } + } else { + // Not cachable - fall back to QMetaObject (eg. dynamic meta object) + // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal. + // XXX This is a workaround for QTBUG-9420. + 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; +} + +FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject); +FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New); +FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New); +FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString); +FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned); +FAST_VALUE_GETTER(Float, float, 0, v8::Number::New); +FAST_VALUE_GETTER(Double, double, 0, v8::Number::New); + +static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This()); + + if (!resource || resource->object.isNull()) + return; + + QObject *object = resource->object; + + uint32_t data = info.Data()->Uint32Value(); + int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter + + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + Q_ASSERT(ddata); + Q_ASSERT(ddata->propertyCache); + + QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index); + Q_ASSERT(pdata); + + Q_ASSERT(pdata->isWritable() || pdata->isQList()); + + StoreProperty(resource->engine, object, pdata, value); +} + +static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *) +{ + Q_ASSERT(handle->IsObject()); + + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject()); + + Q_ASSERT(resource); + + QObject *object = resource->object; + if (object) { + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + if (ddata) { + ddata->v8object.Clear(); + if (!object->parent() && !ddata->indestructible) + object->deleteLater(); + } + } + + // XXX do we want to use the objectDataRefCount to support multiple concurrent engines? + + handle.Dispose(); +} + +v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine) +{ + Q_ASSERT(object); + + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + + Q_ASSERT(ddata && ddata->propertyCache && ddata->propertyCache == this); + Q_ASSERT(ddata->v8object.IsEmpty()); + + // Setup constructor + if (constructor.IsEmpty()) { + v8::Local<v8::FunctionTemplate> ft; + + QString toString = QLatin1String("toString"); + QString destroy = QLatin1String("destroy"); + + // XXX 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 + for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) { + Data *property = *iter; + if (property->isFunction() || !property->isWritable() || + property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF || + property->coreIndex == 0) + continue; + + v8::AccessorGetter fastgetter = 0; + + + if (property->isQObject()) + fastgetter = QObjectValueGetter; + else if (property->propType == QMetaType::Int || property->isEnum()) + fastgetter = IntValueGetter; + else if (property->propType == QMetaType::Bool) + fastgetter = BoolValueGetter; + else if (property->propType == QMetaType::QString) + fastgetter = QStringValueGetter; + else if (property->propType == QMetaType::UInt) + fastgetter = UIntValueGetter; + else if (property->propType == QMetaType::Float) + fastgetter = FloatValueGetter; + else if (property->propType == QMetaType::Double) + fastgetter = DoubleValueGetter; + + if (fastgetter) { + int notifyIndex = property->notifyIndex; + if (property->isConstant()) notifyIndex = 0; + else if (notifyIndex == -1) notifyIndex = 0x7FFF; + uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex; + + 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); + } + + ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter, + v8::Integer::NewFromUnsigned(data)); + } + } + + if (ft.IsEmpty()) { + constructor = v8::Persistent<v8::Function>::New(engine->qobjectWrapper()->m_constructor); + } else { + ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, + QV8QObjectWrapper::Setter, + QV8QObjectWrapper::Query, + 0, + QV8QObjectWrapper::Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + constructor = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } + } + + v8::Local<v8::Object> result = constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + result->SetExternalResource(r); + + ddata->v8object = v8::Persistent<v8::Object>::New(result); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + return result; +} + +v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) +{ + // XXX aakenned QDeclarativeObjectScriptClass::newQObject() does a lot more + + if (!object) + return v8::Null(); + + if (QObjectPrivate::get(object)->wasDeleted) + return v8::Undefined(); + + QDeclarativeData *ddata = QDeclarativeData::get(object, true); + + if (!ddata) + return v8::Undefined(); + + if (ddata->v8object.IsEmpty()) { + + if (ddata->propertyCache) { + return ddata->propertyCache->newQObject(object, m_engine); + } + + // XXX aakenned - NewInstance() is slow for our case + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(m_engine, object); + rv->SetExternalResource(r); + ddata->v8object = v8::Persistent<v8::Object>::New(rv); + } + + // XXX do we have to check that the v8object isn't "owned" by another engine? + + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + return v8::Local<v8::Object>::New(ddata->v8object); +} + +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); +} + +struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject> +{ + QV8QObjectConnectionList(QObject *object, QV8Engine *engine); + ~QV8QObjectConnectionList(); + + struct Connection { + v8::Persistent<v8::Object> thisObject; + v8::Persistent<v8::Function> function; + }; + + QV8Engine *engine; + + typedef QHash<int, QList<Connection> > SlotHash; + SlotHash slotHash; + + virtual void objectDestroyed(QObject *); + virtual int qt_metacall(QMetaObject::Call, int, void **); +}; + +QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine) +: QDeclarativeGuard<QObject>(object), engine(engine) +{ +} + +QV8QObjectConnectionList::~QV8QObjectConnectionList() +{ + for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) { + QList<Connection> &connections = *iter; + for (int ii = 0; ii < connections.count(); ++ii) { + connections[ii].thisObject.Dispose(); + connections[ii].function.Dispose(); + } + } + slotHash.clear(); +} + +void QV8QObjectConnectionList::objectDestroyed(QObject *object) +{ + engine->qobjectWrapper()->m_connections.remove(object); + 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; + QList<Connection> &connections = *iter; + if (connections.isEmpty()) + return -1; + + // XXX optimize + QMetaMethod method = data()->metaObject()->method(index); + Q_ASSERT(method.methodType() == QMetaMethod::Signal); + QList<QByteArray> params = method.parameterTypes(); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + QVarLengthArray<v8::Handle<v8::Value> > args(params.count()); + int argCount = params.count(); + + for (int ii = 0; ii < argCount; ++ii) { + int type = QMetaType::type(params.at(ii).constData()); + if (type == qMetaTypeId<QVariant>()) { + args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); + } else { + args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1])); + } + } + + // XXX what if this list changes or this object is deleted during the calls? + for (int ii = 0; ii < connections.count(); ++ii) { + Connection &connection = connections[ii]; + if (connection.thisObject.IsEmpty()) { + connection.function->Call(engine->global(), argCount, args.data()); + } else { + connection.function->Call(connection.thisObject, argCount, args.data()); + } + } + } + + 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(); + + if (!args.This()->IsFunction()) + V8THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(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, QList<QV8QObjectConnectionList::Connection>()); + QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex); + } + + QV8QObjectConnectionList::Connection connection; + if (!functionThisValue.IsEmpty()) + connection.thisObject = v8::Persistent<v8::Object>::New(functionThisValue->ToObject()); + connection.function = v8::Persistent<v8::Function>::New(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(); + + if (!args.This()->IsFunction()) + V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(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 + + QList<QV8QObjectConnectionList::Connection> &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! + connection.thisObject.Dispose(); + connection.function.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! + connection.thisObject.Dispose(); + connection.function.Dispose(); + connections.removeAt(ii); + return v8::Undefined(); + } + } + } + + return v8::Undefined(); +} + +/*! + 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. +*/ +v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property, + QV8QObjectWrapper::RevisionMode revisionMode) +{ + return GetProperty(m_engine, object, 0, property, 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. +*/ +bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property, + v8::Handle<v8::Value> value, RevisionMode revisionMode) +{ + return SetProperty(m_engine, object, property, value, revisionMode); +} + +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<MetaCallArgument, 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) { + + MetaCallArgument 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(); + + } +} + +static int EnumType(const QMetaObject *meta, const QString &strname) +{ + QByteArray str = strname.toUtf8(); + 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 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, + const QByteArray &conversionTypeName) +{ + 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: + if (!conversionTypeName.endsWith('*')) + return 10; + else + return 0; + } + } 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 QDeclarativePropertyCache::Data * RelatedMethod(QObject *object, + const QDeclarativePropertyCache::Data *current, + QDeclarativePropertyCache::Data &dummy) +{ + QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache; + if (current->relatedIndex == -1) + return 0; + + if (cache) { + return cache->method(current->relatedIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->relatedIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->relatedIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = QMetaMethod_name(method); + for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) { + if (methodName == QMetaMethod_name(mo->method(ii))) { + dummy.relatedIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, + QV8Engine *engine, CallArgs &callArgs) +{ + if (data.flags & QDeclarativePropertyCache::Data::HasArguments) { + + QMetaMethod m = object->metaObject()->method(data.coreIndex); + QList<QByteArray> argTypeNames = m.parameterTypes(); + QVarLengthArray<int, 9> argTypes(argTypeNames.count()); + + // ### Cache + for (int ii = 0; ii < argTypeNames.count(); ++ii) { + argTypes[ii] = QMetaType::type(argTypeNames.at(ii)); + if (argTypes[ii] == QVariant::Invalid) + argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii))); + if (argTypes[ii] == QVariant::Invalid) { + QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii))); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } + } + + if (argTypes.count() > 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, argTypes.count(), + argTypes.data(), 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 QDeclarativePropertyCache::Data &data, + QV8Engine *engine, CallArgs &callArgs) +{ + int argumentCount = callArgs.Length(); + + const QDeclarativePropertyCache::Data *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QDeclarativePropertyCache::Data dummy; + const QDeclarativePropertyCache::Data *attempt = &data; + + do { + QList<QByteArray> methodArgTypeNames; + + if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments) + methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes(); + + int methodArgumentCount = methodArgTypeNames.count(); + + 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; + QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount); + + bool unknownArgument = false; + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii)); + if (methodArgTypes[ii] == QVariant::Invalid) + methodArgTypes[ii] = EnumType(object->metaObject(), + QString::fromLatin1(methodArgTypeNames.at(ii))); + if (methodArgTypes[ii] == QVariant::Invalid) { + unknownArgument = true; + break; + } + methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii)); + } + if (unknownArgument) + continue; // We don't understand all the parameters + + 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 QDeclarativePropertyCache::Data *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) +{ + QDeclarativeData *ddata = QDeclarativeData::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(); + } + } + + QDeclarativePropertyCache::Data method; + + if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) { + if (ddata->propertyCache) { + QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index); + if (!d) + return v8::Undefined(); + method = *d; + } + } + + if (method.coreIndex == -1) { + QMetaMethod mm = object->metaObject()->method(index); + method.load(object->metaObject()->method(index)); + + if (method.coreIndex == -1) + return v8::Undefined(); + } + + if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) { + v8::Handle<v8::Value> rv; + v8::Handle<v8::Object> qmlglobal = args[2]->ToObject(); + + QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal, + resource->engine->contextWrapper()->context(qmlglobal), + resource->engine); + QDeclarativeV8Function *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.relatedIndex == -1) { + return CallPrecise(object, method, resource->engine, callArgs); + } else { + return CallOverloaded(object, method, resource->engine, callArgs); + } +} + +MetaCallArgument::MetaCallArgument() +: type(QVariant::Invalid), isObjectType(false) +{ +} + +MetaCallArgument::~MetaCallArgument() +{ + cleanup(); +} + +void MetaCallArgument::cleanup() +{ + if (type == QMetaType::QString) { + ((QString *)&data)->~QString(); + } else if (type == -1 || type == qMetaTypeId<QVariant>()) { + ((QVariant *)&data)->~QVariant(); + } else if (type == qMetaTypeId<QScriptValue>()) { + ((QScriptValue *)&data)->~QScriptValue(); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + ((QList<QObject *> *)&data)->~QList<QObject *>(); + } +} + +void *MetaCallArgument::dataPtr() +{ + if (type == -1) + return ((QVariant *)data)->data(); + else + return (void *)&data; +} + +void MetaCallArgument::initAsType(int callType) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == 0) return; + + if (callType == qMetaTypeId<QScriptValue>()) { + new (&data) QScriptValue(); + 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) { + *((QObject **)&data) = 0; + type = callType; + } else if (callType == QMetaType::QString) { + new (&data) QString(); + type = callType; + } else if (callType == qMetaTypeId<QVariant>()) { + type = callType; + new (&data) QVariant(); + } else if (callType == qMetaTypeId<QList<QObject *> >()) { + type = callType; + new (&data) QList<QObject *>(); + } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) { + type = callType; + new (&data) v8::Handle<v8::Value>(); + } else { + type = -1; + new (&data) QVariant(callType, (void *)0); + } +} + +void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId<QScriptValue>()) { + new (&data) QScriptValue(); + type = qMetaTypeId<QScriptValue>(); + } else if (callType == QMetaType::Int) { + *((int *)&data) = int(value->Int32Value()); + type = callType; + } else if (callType == QMetaType::UInt) { + *((uint *)&data) = uint(value->Uint32Value()); + type = callType; + } else if (callType == QMetaType::Bool) { + *((bool *)&data) = value->BooleanValue(); + type = callType; + } else if (callType == QMetaType::Double) { + *((double *)&data) = double(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::Float) { + *((float *)&data) = float(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value->IsNull() || value->IsUndefined()) + new (&data) QString(); + else + new (&data) QString(engine->toString(value->ToString())); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + *((QObject **)&data) = engine->toQObject(value); + type = callType; + } else if (callType == qMetaTypeId<QVariant>()) { + new (&data) QVariant(engine->toVariant(value, -1)); + type = callType; + } else if (callType == qMetaTypeId<QList<QObject*> >()) { + QList<QObject *> *list = new (&data) 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) + list->append(engine->toQObject(array->Get(ii))); + } else { + list->append(engine->toQObject(value)); + } + type = callType; + } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) { + new (&data) v8::Handle<v8::Value>(value); + type = callType; + } else { + new (&data) QVariant(); + type = -1; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + QVariant v = engine->toVariant(value, -1); + + if (v.userType() == callType) { + *((QVariant *)&data) = v; + } else if (v.canConvert((QVariant::Type)callType)) { + *((QVariant *)&data) = v; + ((QVariant *)&data)->convert((QVariant::Type)callType); + } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) { + QObject *obj = ep->toQObject(v); + + if (obj) { + const QMetaObject *objMo = obj->metaObject(); + while (objMo && objMo != mo) objMo = objMo->superClass(); + if (!objMo) obj = 0; + } + + *((QVariant *)&data) = QVariant(callType, &obj); + } else { + *((QVariant *)&data) = QVariant(callType, (void *)0); + } + } +} + +v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine) +{ + if (type == qMetaTypeId<QScriptValue>()) { + return v8::Undefined(); + } else if (type == QMetaType::Int) { + return v8::Integer::New(*((int *)&data)); + } else if (type == QMetaType::UInt) { + return v8::Integer::NewFromUnsigned(*((uint *)&data)); + } else if (type == QMetaType::Bool) { + return v8::Boolean::New(*((bool *)&data)); + } else if (type == QMetaType::Double) { + return v8::Number::New(*((double *)&data)); + } else if (type == QMetaType::Float) { + return v8::Number::New(*((float *)&data)); + } else if (type == QMetaType::QString) { + return engine->toString(*((QString *)&data)); + } else if (type == QMetaType::QObjectStar) { + QObject *object = *((QObject **)&data); + if (object) + QDeclarativeData::get(object, true)->setImplicitDestructible(); + return engine->newQObject(object); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX aakenned Can this be more optimal? Just use Array as a prototype and + // implement directly against QList<QObject*>? + QList<QObject *> &list = *(QList<QObject *>*)&data; + 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<QDeclarativeV8Handle>()) { + return *(v8::Handle<v8::Value>*)&data; + } else if (type == -1 || type == qMetaTypeId<QVariant>()) { + QVariant value = *((QVariant *)&data); + v8::Handle<v8::Value> rv = engine->fromVariant(value); + if (QObject *object = engine->toQObject(rv)) + QDeclarativeData::get(object, true)->setImplicitDestructible(); + return rv; + } else { + return v8::Undefined(); + } +} diff --git a/src/declarative/qml/v8/qv8qobjectwrapper_p.h b/src/declarative/qml/v8/qv8qobjectwrapper_p.h new file mode 100644 index 0000000000..f63396945e --- /dev/null +++ b/src/declarative/qml/v8/qv8qobjectwrapper_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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> + +QT_BEGIN_NAMESPACE + +class QObject; +class QV8Engine; +class QV8ObjectResource; +class QDeclarativePropertyCache; +class QV8QObjectConnectionList; +class Q_AUTOTEST_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>); + QObject *toQObject(QV8ObjectResource *); + + enum RevisionMode { IgnoreRevision, CheckRevision }; + v8::Handle<v8::Value> getProperty(QObject *, v8::Handle<v8::String>, RevisionMode); + bool setProperty(QObject *, v8::Handle<v8::String>, v8::Handle<v8::Value>, RevisionMode); + +private: + friend class QDeclarativePropertyCache; + friend class QV8QObjectConnectionList; + + static v8::Handle<v8::Value> GetProperty(QV8Engine *, QObject *, v8::Handle<v8::Value> *, + v8::Handle<v8::String>, QV8QObjectWrapper::RevisionMode); + static bool SetProperty(QV8Engine *, QObject *, v8::Handle<v8::String>, + 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>); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_methodConstructor; + v8::Persistent<v8::String> m_toStringSymbol; + v8::Persistent<v8::String> m_destroySymbol; + v8::Persistent<v8::Object> m_hiddenObject; + QHash<QObject *, QV8QObjectConnectionList *> m_connections; +}; + +QT_END_NAMESPACE + +#endif // QV8QOBJECTWRAPPER_P_H + + diff --git a/src/declarative/qml/v8/qv8stringwrapper.cpp b/src/declarative/qml/v8/qv8stringwrapper.cpp new file mode 100644 index 0000000000..883c4826c5 --- /dev/null +++ b/src/declarative/qml/v8/qv8stringwrapper.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8stringwrapper_p.h" + +QT_BEGIN_NAMESPACE + +class QV8StringResource : public v8::String::ExternalStringResource +{ +public: + QV8StringResource(const QString &str) : str(str) {} + virtual const uint16_t* data() const { return (uint16_t*)str.constData(); } + virtual size_t length() const { return str.length(); } + virtual void Dispose() { delete this; } + + QString str; +}; + +QV8StringWrapper::QV8StringWrapper() +{ +} + +QV8StringWrapper::~QV8StringWrapper() +{ +} + +void QV8StringWrapper::init() +{ +} + +void QV8StringWrapper::destroy() +{ +} + +v8::Local<v8::String> QV8StringWrapper::toString(const QString &qstr) +{ +// return v8::String::NewExternal(new QV8StringResource(qstr)); + return v8::String::New((uint16_t*)qstr.constData(), qstr.length()); +} + +QString QV8StringWrapper::toString(v8::Handle<v8::String> jsstr) +{ + if (jsstr.IsEmpty()) { + return QString(); + } else if (jsstr->IsExternal()) { + QV8StringResource *r = (QV8StringResource *)jsstr->GetExternalStringResource(); + return r->str; + } else { + QString qstr; + qstr.resize(jsstr->Length()); + jsstr->Write((uint16_t*)qstr.data()); + return qstr; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8stringwrapper_p.h b/src/declarative/qml/v8/qv8stringwrapper_p.h new file mode 100644 index 0000000000..8dde5c8c44 --- /dev/null +++ b/src/declarative/qml/v8/qv8stringwrapper_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV8STRINGWRAPPER_P_H +#define QDECLARATIVEV8STRINGWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_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 // QDECLARATIVEV8STRINGWRAPPER_P_H diff --git a/src/declarative/qml/v8/qv8typewrapper.cpp b/src/declarative/qml/v8/qv8typewrapper.cpp new file mode 100644 index 0000000000..2067f73fae --- /dev/null +++ b/src/declarative/qml/v8/qv8typewrapper.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativecontext_p.h> + +QT_BEGIN_NAMESPACE + +class QV8TypeResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(TypeType); + +public: + QV8TypeResource(QV8Engine *engine); + virtual ~QV8TypeResource(); + + QV8TypeWrapper::TypeNameMode mode; + + QDeclarativeGuard<QObject> object; + QDeclarativeType *type; + QDeclarativeTypeNameCache *typeNamespace; +}; + +QV8TypeResource::QV8TypeResource(QV8Engine *engine) +: QV8ObjectResource(engine), mode(QV8TypeWrapper::IncludeEnums), type(0), typeNamespace(0) +{ +} + +QV8TypeResource::~QV8TypeResource() +{ + if (typeNamespace) typeNamespace->release(); +} + +QV8TypeWrapper::QV8TypeWrapper() +: m_engine(0) +{ +} + +QV8TypeWrapper::~QV8TypeWrapper() +{ +} + +void QV8TypeWrapper::destroy() +{ + m_constructor.Dispose(); +} + +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 = v8::Persistent<v8::Function>::New(ft->GetFunction()); +} + +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QDeclarativeType *t, TypeNameMode mode) +{ + Q_ASSERT(t); + // XXX aakenned - NewInstance() is slow for our case + 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; +} + +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QDeclarativeTypeNameCache *t, TypeNameMode mode) +{ + Q_ASSERT(t); + // XXX aakenned - NewInstance() is slow for our case + 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; + rv->SetExternalResource(r); + return rv; +} + +v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + v8::Object::ExternalResource *r = info.This()->GetExternalResource(); + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return v8::Undefined(); + + QV8Engine *v8engine = resource->engine; + QObject *object = resource->object; + + if (resource->type) { + QDeclarativeType *type = resource->type; + + if (QV8Engine::startsWithUpper(property)) { + if (resource->mode == IncludeEnums) { + QString name = v8engine->toString(property); + + // ### Optimize + QByteArray enumName = name.toUtf8(); + const QMetaObject *metaObject = type->baseMetaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + int value = e.keyToValue(enumName.constData()); + if (value != -1) + return v8::Integer::New(value); + } + } + + // Fall through to undefined + + } else if (resource->object) { + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + return v8engine->qobjectWrapper()->getProperty(ao, property, QV8QObjectWrapper::IgnoreRevision); + + // Fall through to undefined + } + + // Fall through to undefined + + } else if (resource->typeNamespace) { + + QDeclarativeTypeNameCache *typeNamespace = resource->typeNamespace; + QDeclarativeTypeNameCache::Data *d = typeNamespace->data(property); + Q_ASSERT(!d || !d->typeNamespace); // Nested namespaces not supported + + if (d && d->type) { + return v8engine->typeWrapper()->newObject(object, d->type, resource->mode); + } else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = typeNamespace->moduleApi()) { + + // XXX QtScript/JSC required + return v8::Undefined(); + + } + + // Fall through to undefined + + } else { + Q_ASSERT(!"Unreachable"); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + v8::Object::ExternalResource *r = info.This()->GetExternalResource(); + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return value; + + QV8Engine *v8engine = resource->engine; + + // XXX module api + + if (resource->type && resource->object) { + QDeclarativeType *type = resource->type; + QObject *object = resource->object; + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + v8engine->qobjectWrapper()->setProperty(ao, property, value, QV8QObjectWrapper::IgnoreRevision); + } + + return value; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8typewrapper_p.h b/src/declarative/qml/v8/qv8typewrapper_p.h new file mode 100644 index 0000000000..166bf3b7fa --- /dev/null +++ b/src/declarative/qml/v8/qv8typewrapper_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 QDeclarativeType; +class QDeclarativeTypeNameCache; +class QV8TypeWrapper +{ +public: + QV8TypeWrapper(); + ~QV8TypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + enum TypeNameMode { IncludeEnums, ExcludeEnums }; + v8::Local<v8::Object> newObject(QObject *, QDeclarativeType *, TypeNameMode = IncludeEnums); + v8::Local<v8::Object> newObject(QObject *, QDeclarativeTypeNameCache *, TypeNameMode = IncludeEnums); + +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/declarative/qml/v8/qv8valuetypewrapper.cpp b/src/declarative/qml/v8/qv8valuetypewrapper.cpp new file mode 100644 index 0000000000..6dfb4312bc --- /dev/null +++ b/src/declarative/qml/v8/qv8valuetypewrapper.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8valuetypewrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qdeclarativevaluetype_p.h> +#include <private/qdeclarativebinding_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; + QDeclarativeValueType *type; +}; + +class QV8ValueTypeReferenceResource : public QV8ValueTypeResource +{ +public: + QV8ValueTypeReferenceResource(QV8Engine *engine); + + QDeclarativeGuard<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() +{ + m_constructor.Dispose(); +} + +void QV8ValueTypeWrapper::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 = v8::Persistent<v8::Function>::New(ft->GetFunction()); +} + +v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QDeclarativeValueType *type) +{ + // XXX aakenned - NewInstance() is slow for our case + 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, QDeclarativeValueType *type) +{ + // XXX aakenned - NewInstance() is slow for our case + 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; + } +} + +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::Undefined(); + + // XXX aakenned - this is horribly inefficient. People seem to have taken a + // liking to value type properties, so we should probably try and optimize it + // a little. + + QByteArray propName = r->engine->toString(property).toUtf8(); + int index = r->type->metaObject()->indexOfProperty(propName.constData()); + if (index == -1) + return v8::Undefined(); + + if (r->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r); + + if (!reference->object) + return v8::Undefined(); + + 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); + } + + QMetaProperty prop = r->type->metaObject()->property(index); + QVariant result = prop.read(r->type); + + return r->engine->fromVariant(result); +} + +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); + + QDeclarativeBinding *newBinding = 0; + + if (value->IsFunction()) { + QDeclarativeContextData *context = r->engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + QDeclarativePropertyCache::Data cacheData; + cacheData.flags = QDeclarativePropertyCache::Data::IsWritable; + cacheData.propType = reference->object->metaObject()->property(reference->property).userType(); + cacheData.coreIndex = reference->property; + + QDeclarativePropertyCache::ValueTypeData valueTypeData; + valueTypeData.valueTypeCoreIdx = index; + valueTypeData.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(); + QString url = r->engine->toString(frame->GetScriptName()); + + newBinding = new QDeclarativeBinding(&function, reference->object, context); + newBinding->setSourceLocation(url, lineNumber); + newBinding->setTarget(QDeclarativePropertyPrivate::restore(cacheData, valueTypeData, + reference->object, context)); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject); + } + + QDeclarativeAbstractBinding *oldBinding = + QDeclarativePropertyPrivate::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/declarative/qml/v8/qv8valuetypewrapper_p.h b/src/declarative/qml/v8/qv8valuetypewrapper_p.h new file mode 100644 index 0000000000..b4bc90cfec --- /dev/null +++ b/src/declarative/qml/v8/qv8valuetypewrapper_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 <QtDeclarative/qdeclarativelist.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QDeclarativeValueType; +class QV8ValueTypeWrapper +{ +public: + QV8ValueTypeWrapper(); + ~QV8ValueTypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> newValueType(QObject *, int, QDeclarativeValueType *); + v8::Local<v8::Object> newValueType(const QVariant &, QDeclarativeValueType *); + + 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); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8VALUETYPEWRAPPER_P_H + + diff --git a/src/declarative/qml/v8/qv8variantwrapper.cpp b/src/declarative/qml/v8/qv8variantwrapper.cpp new file mode 100644 index 0000000000..2d91a335b2 --- /dev/null +++ b/src/declarative/qml/v8/qv8variantwrapper.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8variantwrapper_p.h" +#include "qv8engine_p.h" +#include "qdeclarativeengine_p.h" + +QT_BEGIN_NAMESPACE + +class QV8VariantResource : public QV8ObjectResource, + public QDeclarativeEnginePrivate::ScarceResourceData +{ + V8_RESOURCE_TYPE(VariantType); +public: + QV8VariantResource(QV8Engine *engine, const QVariant &data); +}; + +QV8VariantResource::QV8VariantResource(QV8Engine *engine, const QVariant &data) +: QV8ObjectResource(engine), QDeclarativeEnginePrivate::ScarceResourceData(data) +{ +} + +QV8VariantWrapper::QV8VariantWrapper() +: m_engine(0) +{ +} + +QV8VariantWrapper::~QV8VariantWrapper() +{ +} + +void QV8VariantWrapper::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 = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } + { + m_preserve = v8::Persistent<v8::Function>::New(v8::FunctionTemplate::New(Preserve)->GetFunction()); + m_destroy = v8::Persistent<v8::Function>::New(v8::FunctionTemplate::New(Destroy)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + 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)); + m_scarceConstructor = v8::Persistent<v8::Function>::New(ft->GetFunction()); + } + +} + +void QV8VariantWrapper::destroy() +{ + m_destroy.Dispose(); + m_preserve.Dispose(); + m_scarceConstructor.Dispose(); + m_constructor.Dispose(); +} + +v8::Local<v8::Object> QV8VariantWrapper::newVariant(const QVariant &value) +{ + bool scarceResource = value.type() == QVariant::Pixmap || + value.type() == QVariant::Image; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_engine->engine()); + + // XXX aakenned - NewInstance() is slow for our case + v8::Local<v8::Object> rv; + QV8VariantResource *r = new QV8VariantResource(m_engine, value); + + if (scarceResource) { + Q_ASSERT(ep->scarceResourcesRefCount); + rv = m_scarceConstructor->NewInstance(); + 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; +} + +v8::Handle<v8::Value> QV8VariantWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + return v8::Undefined(); +} + +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::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(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8variantwrapper_p.h b/src/declarative/qml/v8/qv8variantwrapper_p.h new file mode 100644 index 0000000000..391ec4c21a --- /dev/null +++ b/src/declarative/qml/v8/qv8variantwrapper_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 <QtDeclarative/qdeclarativelist.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>); + 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> 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> Preserve(const v8::Arguments &args); + static v8::Handle<v8::Value> Destroy(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; +}; + +QT_END_NAMESPACE + +#endif // QV8VARIANTWRAPPER_P_H + diff --git a/src/declarative/qml/v8/qv8worker.cpp b/src/declarative/qml/v8/qv8worker.cpp new file mode 100644 index 0000000000..6b6ecd8c2a --- /dev/null +++ b/src/declarative/qml/v8/qv8worker.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8worker_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 +}; + +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 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; +} + +// XXX double check exception safety + +#include <QDebug> +#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 + 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 { + push(data, valueheader(WorkerUndefined)); + } + + // XXX Need to serialize QDeclarativeListModel + /* + QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(value.toQObject()); + if (lm) { + QDeclarativeListModelWorkerAgent *agent = lm->agent(); + if (agent) { + QDeclarativeListModelWorkerAgent::VariantRef v(agent); + return QVariant::fromValue(v); + } else { + return QVariant(); + } + } + */ +} + +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); + } + } + 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/declarative/qml/v8/qv8worker_p.h b/src/declarative/qml/v8/qv8worker_p.h new file mode 100644 index 0000000000..086e18e7e0 --- /dev/null +++ b/src/declarative/qml/v8/qv8worker_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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/declarative/qml/v8/v8.pri b/src/declarative/qml/v8/v8.pri new file mode 100644 index 0000000000..d91acd7759 --- /dev/null +++ b/src/declarative/qml/v8/v8.pri @@ -0,0 +1,32 @@ +INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv8_p.h \ + $$PWD/qv8stringwrapper_p.h \ + $$PWD/qv8engine_p.h \ + $$PWD/qhashedstring_p.h \ + $$PWD/qv8contextwrapper_p.h \ + $$PWD/qv8qobjectwrapper_p.h \ + $$PWD/qv8typewrapper_p.h \ + $$PWD/qv8listwrapper_p.h \ + $$PWD/qv8variantwrapper_p.h \ + $$PWD/qv8valuetypewrapper_p.h \ + $$PWD/qv8include_p.h \ + $$PWD/qv8worker_p.h \ + $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ + +SOURCES += \ + $$PWD/qv8stringwrapper.cpp \ + $$PWD/qv8engine.cpp \ + $$PWD/qhashedstring.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/../../../3rdparty/javascriptcore/DateMath.cpp \ + |