diff options
Diffstat (limited to 'src/qml/qml/v8/qv8engine_p.h')
-rw-r--r-- | src/qml/qml/v8/qv8engine_p.h | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h new file mode 100644 index 0000000000..22a8d7599f --- /dev/null +++ b/src/qml/qml/v8/qv8engine_p.h @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLV8ENGINE_P_H +#define QQMLV8ENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <QtCore/qset.h> +#include <QtCore/qmutex.h> +#include <QtCore/qstack.h> +#include <QtCore/qstringlist.h> +#include <QtCore/QElapsedTimer> +#include <QtCore/QThreadStorage> + +#include <private/qv8_p.h> +#include <qjsengine.h> +#include <qjsvalue.h> +#include "qjsvalue_p.h" +#include "qjsvalueiterator_p.h" +#include "qscriptoriginalglobalobject_p.h" +#include "qscripttools_p.h" + +#include <private/qqmlpropertycache_p.h> + +#include "qv8contextwrapper_p.h" +#include "qv8qobjectwrapper_p.h" +#include "qv8stringwrapper_p.h" +#include "qv8typewrapper_p.h" +#include "qv8listwrapper_p.h" +#include "qv8variantwrapper_p.h" +#include "qv8valuetypewrapper_p.h" +#include "qv8sequencewrapper_p.h" + +QT_BEGIN_NAMESPACE + + +// Uncomment the following line to enable global handle debugging. When enabled, all the persistent +// handles allocated using qPersistentNew() (or registered with qPersistentRegsiter()) and disposed +// with qPersistentDispose() are tracked. If you try and do something illegal, like double disposing +// a handle, qFatal() is called. +// #define QML_GLOBAL_HANDLE_DEBUGGING + +#define V8_RESOURCE_TYPE(resourcetype) \ +public: \ + enum { V8ResourceType = QV8ObjectResource:: resourcetype }; \ + virtual QV8ObjectResource::ResourceType resourceType() const { return QV8ObjectResource:: resourcetype; } \ +private: + +#define V8ENGINE() ((QV8Engine *)v8::External::Unwrap(args.Data())) +#define V8FUNCTION(function, engine) v8::FunctionTemplate::New(function, v8::External::Wrap((QV8Engine*)engine))->GetFunction() +#define V8THROW_ERROR(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8THROW_TYPE(string) { \ + v8::ThrowException(v8::Exception::TypeError(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8ENGINE_ACCESSOR() ((QV8Engine *)v8::External::Unwrap(info.Data())); +#define V8THROW_ERROR_SETTER(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return; \ +} + +#define V8_DEFINE_EXTENSION(dataclass, datafunction) \ + static inline dataclass *datafunction(QV8Engine *engine) \ + { \ + static int extensionId = -1; \ + if (extensionId == -1) { \ + QV8Engine::registrationMutex()->lock(); \ + if (extensionId == -1) \ + extensionId = QV8Engine::registerExtension(); \ + QV8Engine::registrationMutex()->unlock(); \ + } \ + dataclass *rv = (dataclass *)engine->extensionData(extensionId); \ + if (!rv) { \ + rv = new dataclass(engine); \ + engine->setExtensionData(extensionId, rv); \ + } \ + return rv; \ + } \ + + +class QV8Engine; +class QV8ObjectResource : public v8::Object::ExternalResource +{ +public: + QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); } + enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, + ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, + ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, + ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType, + SequenceType, LocaleDataType }; + virtual ResourceType resourceType() const = 0; + + QV8Engine *engine; +}; + +template<class T> +inline T *v8_resource_cast(v8::Handle<v8::Object> object) { + QV8ObjectResource *resource = static_cast<QV8ObjectResource *>(object->GetExternalResource()); + return (resource && (quint32)resource->resourceType() == (quint32)T::V8ResourceType)?static_cast<T *>(resource):0; +} + +template<class T> +inline T *v8_resource_check(v8::Handle<v8::Object> object) { + T *resource = static_cast<T *>(object->GetExternalResource()); + Q_ASSERT(resource && resource->resourceType() == (quint32)T::V8ResourceType); + return resource; +} + +// Used to allow a QObject method take and return raw V8 handles without having to expose +// v8 in the public API. +// Use like this: +// class MyClass : public QObject { +// Q_OBJECT +// ... +// Q_INVOKABLE void myMethod(QQmlV8Function*); +// }; +// The QQmlV8Function - and consequently the arguments and return value - only remains +// valid during the call. If the return value isn't set within myMethod(), the will return +// undefined. +class QV8Engine; +class QQmlV8Function +{ +public: + int Length() const { return _ac; } + v8::Local<v8::Value> operator[](int idx) { return (*_a)->Get(idx); } + QQmlContextData *context() { return _c; } + v8::Handle<v8::Object> qmlGlobal() { return *_g; } + void returnValue(v8::Handle<v8::Value> rv) { *_r = rv; } + QV8Engine *engine() const { return _e; } +private: + friend class QV8QObjectWrapper; + QQmlV8Function(); + QQmlV8Function(const QQmlV8Function &); + QQmlV8Function &operator=(const QQmlV8Function &); + + QQmlV8Function(int length, v8::Handle<v8::Object> &args, + v8::Handle<v8::Value> &rv, v8::Handle<v8::Object> &global, + QQmlContextData *c, QV8Engine *e) + : _ac(length), _a(&args), _r(&rv), _g(&global), _c(c), _e(e) {} + + int _ac; + v8::Handle<v8::Object> *_a; + v8::Handle<v8::Value> *_r; + v8::Handle<v8::Object> *_g; + QQmlContextData *_c; + QV8Engine *_e; +}; + +class QQmlV8Handle +{ +public: + QQmlV8Handle() : d(0) {} + QQmlV8Handle(const QQmlV8Handle &other) : d(other.d) {} + QQmlV8Handle &operator=(const QQmlV8Handle &other) { d = other.d; return *this; } + + static QQmlV8Handle fromHandle(v8::Handle<v8::Value> h) { + return QQmlV8Handle(*h); + } + v8::Handle<v8::Value> toHandle() const { + return v8::Handle<v8::Value>((v8::Value *)d); + } +private: + QQmlV8Handle(void *d) : d(d) {} + void *d; +}; + +class QObject; +class QQmlEngine; +class QQmlValueType; +class QNetworkAccessManager; +class QQmlContextData; + +class Q_AUTOTEST_EXPORT QV8GCCallback +{ +private: + class ThreadData; +public: + static void garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags); + static void registerGcPrologueCallback(); + + class Q_AUTOTEST_EXPORT Node { + public: + typedef void (*PrologueCallback)(Node *node); + Node(PrologueCallback callback); + ~Node(); + + QIntrusiveListNode node; + PrologueCallback prologueCallback; + }; + + static void addGcCallbackNode(Node *node); +}; + +class Q_QML_EXPORT QV8Engine +{ +public: + static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } + static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } + + QV8Engine(QJSEngine* qq,QJSEngine::ContextOwnership ownership = QJSEngine::CreateNewContext); + virtual ~QV8Engine(); + + // This enum should be in sync with QQmlEngine::ObjectOwnership + enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; + + struct Deletable { + virtual ~Deletable() {} + }; + + class Exception + { + typedef QPair<v8::Persistent<v8::Value>, v8::Persistent<v8::Message> > ValueMessagePair; + + v8::Persistent<v8::Value> m_value; + v8::Persistent<v8::Message> m_message; + QStack<ValueMessagePair> m_stack; + + Q_DISABLE_COPY(Exception) + public: + inline Exception(); + inline ~Exception(); + inline void set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message); + inline void clear(); + inline operator bool() const; + inline operator v8::Handle<v8::Value>() const; + inline int lineNumber() const; + inline QStringList backtrace() const; + + inline void push(); + inline void pop(); + }; + + void initDeclarativeGlobalObject(); + void setEngine(QQmlEngine *engine); + QQmlEngine *engine() { return m_engine; } + v8::Local<v8::Object> global() { return m_context->Global(); } + v8::Handle<v8::Context> context() const { return m_context; } + + inline void registerValue(QJSValuePrivate *data); + inline void unregisterValue(QJSValuePrivate *data); + inline void invalidateAllValues(); + + inline void registerValueIterator(QJSValueIteratorPrivate *data); + inline void unregisterValueIterator(QJSValueIteratorPrivate *data); + inline void invalidateAllIterators(); + + QV8ContextWrapper *contextWrapper() { return &m_contextWrapper; } + QV8QObjectWrapper *qobjectWrapper() { return &m_qobjectWrapper; } + QV8TypeWrapper *typeWrapper() { return &m_typeWrapper; } + QV8ListWrapper *listWrapper() { return &m_listWrapper; } + QV8VariantWrapper *variantWrapper() { return &m_variantWrapper; } + QV8ValueTypeWrapper *valueTypeWrapper() { return &m_valueTypeWrapper; } + QV8SequenceWrapper *sequenceWrapper() { return &m_sequenceWrapper; } + + void *xmlHttpRequestData() { return m_xmlHttpRequestData; } + + Deletable *listModelData() { return m_listModelData; } + void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } + + QQmlContextData *callingContext(); + + v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>); + inline QJSValuePrivate::PropertyFlags getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property); + void freezeObject(v8::Handle<v8::Value>); + + inline QString toString(v8::Handle<v8::Value> string); + inline QString toString(v8::Handle<v8::String> string); + static QString toStringStatic(v8::Handle<v8::Value>); + static QString toStringStatic(v8::Handle<v8::String>); + static inline bool startsWithUpper(v8::Handle<v8::String>); + + QVariant toVariant(v8::Handle<v8::Value>, int typeHint); + v8::Handle<v8::Value> fromVariant(const QVariant &); + inline bool isVariant(v8::Handle<v8::Value>); + + // Compile \a source (from \a fileName at \a lineNumber) in QML mode + v8::Local<v8::Script> qmlModeCompile(const QString &source, + const QString &fileName = QString(), + int lineNumber = 1); + v8::Local<v8::Script> qmlModeCompile(const char *source, int sourceLength = -1, + const QString &fileName = QString(), + int lineNumber = 1); + + // Return the QML global "scope" object for the \a ctxt context and \a scope object. + inline v8::Local<v8::Object> qmlScope(QQmlContextData *ctxt, QObject *scope); + + // Return a JS wrapper for the given QObject \a object + inline v8::Handle<v8::Value> newQObject(QObject *object); + inline v8::Handle<v8::Value> newQObject(QObject *object, const ObjectOwnership ownership); + inline bool isQObject(v8::Handle<v8::Value>); + inline QObject *toQObject(v8::Handle<v8::Value>); + + // Return a JS string for the given QString \a string + inline v8::Local<v8::String> toString(const QString &string); + + // Create a new value type object + inline v8::Handle<v8::Value> newValueType(QObject *, int coreIndex, QQmlValueType *); + inline v8::Handle<v8::Value> newValueType(const QVariant &, QQmlValueType *); + + // Create a new sequence type object + inline v8::Handle<v8::Value> newSequence(int sequenceType, QObject *, int coreIndex, bool *succeeded); + + // Create a new QVariant object. This doesn't examine the type of the variant, but always returns + // a QVariant wrapper + inline v8::Handle<v8::Value> newQVariant(const QVariant &); + + // Return the network access manager for this engine. By default this returns the network + // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 + // instance (like in WorkerScript). + virtual QNetworkAccessManager *networkAccessManager(); + + // Return the list of illegal id names (the names of the properties on the global object) + const QStringHash<bool> &illegalNames() const; + + inline void collectGarbage() { gc(); } + static void gc(); + + void clearExceptions(); + void setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message = v8::Handle<v8::Message>()); + v8::Handle<v8::Value> throwException(v8::Handle<v8::Value> value); + bool hasUncaughtException() const; + int uncaughtExceptionLineNumber() const; + QStringList uncaughtExceptionBacktrace() const; + v8::Handle<v8::Value> uncaughtException() const; + void saveException(); + void restoreException(); + +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + // Used for handle debugging + static void registerHandle(void *); + static void releaseHandle(void *); +#endif + + static QMutex *registrationMutex(); + static int registerExtension(); + + inline Deletable *extensionData(int) const; + void setExtensionData(int, Deletable *); + + inline v8::Handle<v8::Value> makeJSValue(bool value); + inline v8::Local<v8::Value> makeJSValue(int value); + inline v8::Local<v8::Value> makeJSValue(uint value); + inline v8::Local<v8::Value> makeJSValue(double value); + inline v8::Handle<v8::Value> makeJSValue(QJSValue::SpecialValue value); + inline v8::Local<v8::Value> makeJSValue(const QString &value); + + inline QScriptPassPointer<QJSValuePrivate> evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + QScriptPassPointer<QJSValuePrivate> evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch); + + QScriptPassPointer<QJSValuePrivate> newArray(uint length); + v8::Local<v8::Object> newVariant(const QVariant &variant); + + v8::Local<v8::Array> variantListToJS(const QVariantList &lst); + QVariantList variantListFromJS(v8::Handle<v8::Array> jsArray); + + v8::Local<v8::Object> variantMapToJS(const QVariantMap &vmap); + QVariantMap variantMapFromJS(v8::Handle<v8::Object> jsObject); + + v8::Handle<v8::Value> variantToJS(const QVariant &value); + QVariant variantFromJS(v8::Handle<v8::Value> value); + + v8::Handle<v8::Value> metaTypeToJS(int type, const void *data); + bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data); + + bool convertToNativeQObject(v8::Handle<v8::Value> value, + const QByteArray &targetType, + void **result); + + QVariant &variantValue(v8::Handle<v8::Value> value); + + QJSValue scriptValueFromInternal(v8::Handle<v8::Value>) const; + + void emitSignalHandlerException(); + + // used for console.time(), console.timeEnd() + void startTimer(const QString &timerName); + qint64 stopTimer(const QString &timerName, bool *wasRunning); + + // used for console.count() + int consoleCountHelper(const QString &file, int line, int column); + + QObject *qtObjectFromJS(v8::Handle<v8::Value> value); + QSet<int> visitedConversionObjects; + + static QDateTime qtDateTimeFromJsDate(double jsDate); + + void addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle); + void addRelationshipForGC(QObject *object, QObject *other); + + struct ThreadData { + ThreadData(); + ~ThreadData(); + v8::Isolate* isolate; + bool gcPrologueCallbackRegistered; + QIntrusiveList<QV8GCCallback::Node, &QV8GCCallback::Node::node> gcCallbackNodes; + }; + + static bool hasThreadData(); + static ThreadData* threadData(); + static void ensurePerThreadIsolate(); + + v8::Persistent<v8::Object> m_strongReferencer; + +protected: + QJSEngine* q; + QQmlEngine *m_engine; + bool m_ownsV8Context; + v8::Persistent<v8::Context> m_context; + QScriptOriginalGlobalObject m_originalGlobalObject; + + QV8StringWrapper m_stringWrapper; + QV8ContextWrapper m_contextWrapper; + QV8QObjectWrapper m_qobjectWrapper; + QV8TypeWrapper m_typeWrapper; + QV8ListWrapper m_listWrapper; + QV8VariantWrapper m_variantWrapper; + QV8ValueTypeWrapper m_valueTypeWrapper; + QV8SequenceWrapper m_sequenceWrapper; + + v8::Persistent<v8::Function> m_getOwnPropertyNames; + v8::Persistent<v8::Function> m_freezeObject; + + void *m_xmlHttpRequestData; + + QVector<Deletable *> m_extensionData; + Deletable *m_listModelData; + + QStringHash<bool> m_illegalNames; + + Exception m_exception; + + QElapsedTimer m_time; + QHash<QString, qint64> m_startedTimers; + + QHash<QString, quint32> m_consoleCount; + + QVariant toBasicVariant(v8::Handle<v8::Value>); + + void initializeGlobal(v8::Handle<v8::Object>); + + double qtDateTimeToJsDate(const QDateTime &dt); + +private: + static v8::Persistent<v8::Object> *findOwnerAndStrength(QObject *object, bool *shouldBeStrong); + + typedef QScriptIntrusiveList<QJSValuePrivate, &QJSValuePrivate::m_node> ValueList; + ValueList m_values; + typedef QScriptIntrusiveList<QJSValueIteratorPrivate, &QJSValueIteratorPrivate::m_node> ValueIteratorList; + ValueIteratorList m_valueIterators; + + Q_DISABLE_COPY(QV8Engine) +}; + +// Allocate a new Persistent handle. *ALL* persistent handles in QML must be allocated +// using this method. +template<class T> +v8::Persistent<T> qPersistentNew(v8::Handle<T> that) +{ + v8::Persistent<T> rv = v8::Persistent<T>::New(that); +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*rv); +#endif + return rv; +} + +// Register a Persistent handle that was returned to you by V8 (such as by +// v8::Context::New). This allows us to do handle tracking on these handles too. +template<class T> +void qPersistentRegister(v8::Persistent<T> handle) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*handle); +#else + Q_UNUSED(handle); +#endif +} + +// Dispose and clear a persistent handle. *ALL* persistent handles in QML must be +// disposed using this method. +template<class T> +void qPersistentDispose(v8::Persistent<T> &that) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::releaseHandle(*that); +#endif + that.Dispose(); + that.Clear(); +} + +QString QV8Engine::toString(v8::Handle<v8::Value> string) +{ + return m_stringWrapper.toString(string->ToString()); +} + +QString QV8Engine::toString(v8::Handle<v8::String> string) +{ + return m_stringWrapper.toString(string); +} + +bool QV8Engine::isVariant(v8::Handle<v8::Value> value) +{ + return m_variantWrapper.isVariant(value); +} + +v8::Local<v8::Object> QV8Engine::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + return m_contextWrapper.qmlScope(ctxt, scope); +} + +bool QV8Engine::isQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.isQObject(v8::Handle<v8::Object>::Cast(obj)):false; +} + +QObject *QV8Engine::toQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.toQObject(v8::Handle<v8::Object>::Cast(obj)):0; +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object) +{ + return m_qobjectWrapper.newQObject(object); +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object, const ObjectOwnership ownership) +{ + if (!object) + return v8::Null(); + + v8::Handle<v8::Value> result = newQObject(object); + QQmlData *ddata = QQmlData::get(object, true); + if (ownership == JavaScriptOwnership && ddata) { + ddata->indestructible = false; + ddata->explicitIndestructibleSet = true; + } + return result; +} + +v8::Local<v8::String> QV8Engine::toString(const QString &string) +{ + return m_stringWrapper.toString(string); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(QObject *object, int property, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(object, property, type); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(const QVariant &value, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(value, type); +} + +v8::Handle<v8::Value> QV8Engine::newSequence(int sequenceType, QObject *object, int property, bool *succeeded) +{ + return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded); +} + +// XXX Can this be made more optimal? It is called prior to resolving each and every +// unqualified name in QV8ContextWrapper. +bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string) +{ + uint16_t c = string->GetCharacter(0); + return (c >= 'A' && c <= 'Z') || + (c > 127 && QChar::category(c) == QChar::Letter_Uppercase); +} + +QV8Engine::Deletable *QV8Engine::extensionData(int index) const +{ + if (index < m_extensionData.count()) + return m_extensionData[index]; + else + return 0; +} + +QT_END_NAMESPACE + +#endif // QQMLV8ENGINE_P_H |