/**************************************************************************** ** ** Copyright (C) 2012 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$ ** 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 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 #include #include #include #include #include #include #include #include #include #include #include "qjsvalue_p.h" #include "qjsvalueiterator_p.h" #include "qscriptoriginalglobalobject_p.h" #include "qscripttools_p.h" #include #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(); \ } #define V8THROW_TYPE(string) { \ v8::ThrowException(v8::Exception::TypeError(v8::String::New(string))); \ return v8::Handle(); \ } #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) \ 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 inline T *v8_resource_cast(v8::Handle object) { QV8ObjectResource *resource = static_cast(object->GetExternalResource()); return (resource && (quint32)resource->resourceType() == (quint32)T::V8ResourceType)?static_cast(resource):0; } template inline T *v8_resource_check(v8::Handle object) { T *resource = static_cast(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(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 operator[](int idx) { return (*_a)->Get(idx); } QDeclarativeContextData *context() { return _c; } v8::Handle qmlGlobal() { return *_g; } void returnValue(v8::Handle 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 &args, v8::Handle &rv, v8::Handle &global, QDeclarativeContextData *c, QV8Engine *e) : _ac(length), _a(&args), _r(&rv), _g(&global), _c(c), _e(e) {} int _ac; v8::Handle *_a; v8::Handle *_r; v8::Handle *_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 h) { return QDeclarativeV8Handle(*h); } v8::Handle toHandle() const { return v8::Handle((v8::Value *)d); } private: QDeclarativeV8Handle(void *d) : d(d) {} void *d; }; class QObject; class QDeclarativeEngine; class QDeclarativeValueType; class QNetworkAccessManager; class QDeclarativeContextData; 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_DECLARATIVE_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); ~QV8Engine(); // ### TODO get rid of it, do we really need CppOwnership? // This enum should be in sync with QDeclarativeEngine::ObjectOwnership enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; struct Deletable { virtual ~Deletable() {} }; class Exception { typedef QPair, v8::Persistent > ValueMessagePair; v8::Persistent m_value; v8::Persistent m_message; QStack m_stack; Q_DISABLE_COPY(Exception) public: inline Exception(); inline ~Exception(); inline void set(v8::Handle value, v8::Handle message); inline void clear(); inline operator bool() const; inline operator v8::Handle() const; inline int lineNumber() const; inline QStringList backtrace() const; inline void push(); inline void pop(); }; void initDeclarativeGlobalObject(); void setEngine(QDeclarativeEngine *engine); QDeclarativeEngine *engine() { return m_engine; } v8::Local global() { return m_context->Global(); } v8::Handle 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; } void *sqlDatabaseData() { return m_sqlDatabaseData; } Deletable *listModelData() { return m_listModelData; } void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } QDeclarativeContextData *callingContext(); v8::Local getOwnPropertyNames(v8::Handle); inline QJSValue::PropertyFlags getPropertyFlags(v8::Handle object, v8::Handle property); void freezeObject(v8::Handle); inline QString toString(v8::Handle string); inline QString toString(v8::Handle string); static QString toStringStatic(v8::Handle); static QString toStringStatic(v8::Handle); static inline bool startsWithUpper(v8::Handle); QVariant toVariant(v8::Handle, int typeHint); v8::Handle fromVariant(const QVariant &); inline bool isVariant(v8::Handle); // Compile \a source (from \a fileName at \a lineNumber) in QML mode v8::Local 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 qmlScope(QDeclarativeContextData *ctxt, QObject *scope); QScriptPassPointer newRegExp(const QRegExp ®exp); QScriptPassPointer newRegExp(const QString &pattern, const QString &flags); // Return a JS wrapper for the given QObject \a object inline v8::Handle newQObject(QObject *object); inline v8::Handle newQObject(QObject *object, const ObjectOwnership ownership); inline bool isQObject(v8::Handle); inline QObject *toQObject(v8::Handle); // Return a JS string for the given QString \a string inline v8::Local toString(const QString &string); // Create a new value type object inline v8::Handle newValueType(QObject *, int coreIndex, QDeclarativeValueType *); inline v8::Handle newValueType(const QVariant &, QDeclarativeValueType *); // Create a new sequence type object inline v8::Handle 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 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 QStringHash &illegalNames() const; inline void collectGarbage() { gc(); } static void gc(); void clearExceptions(); void setException(v8::Handle value, v8::Handle message = v8::Handle()); v8::Handle throwException(v8::Handle value); bool hasUncaughtException() const; int uncaughtExceptionLineNumber() const; QStringList uncaughtExceptionBacktrace() const; v8::Handle 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 makeJSValue(bool value); inline v8::Local makeJSValue(int value); inline v8::Local makeJSValue(uint value); inline v8::Local makeJSValue(double value); inline v8::Handle makeJSValue(QJSValue::SpecialValue value); inline v8::Local makeJSValue(const QString &value); inline QScriptPassPointer evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); QScriptPassPointer evaluate(v8::Handle script, v8::TryCatch& tryCatch); QScriptPassPointer newArray(uint length); v8::Local newVariant(const QVariant &variant); QScriptPassPointer newVariant(QJSValuePrivate* value, const QVariant &variant); v8::Local variantListToJS(const QVariantList &lst); QVariantList variantListFromJS(v8::Handle jsArray); v8::Local variantMapToJS(const QVariantMap &vmap); QVariantMap variantMapFromJS(v8::Handle jsObject); v8::Handle variantToJS(const QVariant &value); QVariant variantFromJS(v8::Handle value); v8::Handle metaTypeToJS(int type, const void *data); bool metaTypeFromJS(v8::Handle value, int type, void *data); bool convertToNativeQObject(v8::Handle value, const QByteArray &targetType, void **result); QVariant &variantValue(v8::Handle value); QJSValue scriptValueFromInternal(v8::Handle) const; void emitSignalHandlerException(); // used for console.time(), console.timeEnd() void startTimer(const QString &timerName); qint64 stopTimer(const QString &timerName, bool *wasRunning); QObject *qtObjectFromJS(v8::Handle value); QSet visitedConversionObjects; static QDateTime qtDateTimeFromJsDate(double jsDate); void addRelationshipForGC(QObject *object, v8::Persistent handle); void addRelationshipForGC(QObject *object, QObject *other); struct ThreadData { ThreadData(); ~ThreadData(); v8::Isolate* isolate; bool gcPrologueCallbackRegistered; QIntrusiveList gcCallbackNodes; }; static bool hasThreadData(); static ThreadData* threadData(); static void ensurePerThreadIsolate(); v8::Persistent m_strongReferencer; protected: QJSEngine* q; QDeclarativeEngine *m_engine; bool m_ownsV8Context; v8::Persistent 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 m_getOwnPropertyNames; v8::Persistent m_freezeObject; void *m_xmlHttpRequestData; void *m_sqlDatabaseData; QVector m_extensionData; Deletable *m_listModelData; QStringHash m_illegalNames; Exception m_exception; QElapsedTimer m_time; QHash m_startedTimers; QVariant toBasicVariant(v8::Handle); void initializeGlobal(v8::Handle); double qtDateTimeToJsDate(const QDateTime &dt); private: static v8::Persistent *findOwnerAndStrength(QObject *object, bool *shouldBeStrong); typedef QScriptIntrusiveList ValueList; ValueList m_values; typedef QScriptIntrusiveList 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 v8::Persistent qPersistentNew(v8::Handle that) { v8::Persistent rv = v8::Persistent::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 void qPersistentRegister(v8::Persistent 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 void qPersistentDispose(v8::Persistent &that) { #ifdef QML_GLOBAL_HANDLE_DEBUGGING QV8Engine::releaseHandle(*that); #endif that.Dispose(); that.Clear(); } QString QV8Engine::toString(v8::Handle string) { return m_stringWrapper.toString(string->ToString()); } QString QV8Engine::toString(v8::Handle string) { return m_stringWrapper.toString(string); } bool QV8Engine::isVariant(v8::Handle value) { return m_variantWrapper.isVariant(value); } v8::Local QV8Engine::qmlScope(QDeclarativeContextData *ctxt, QObject *scope) { return m_contextWrapper.qmlScope(ctxt, scope); } bool QV8Engine::isQObject(v8::Handle obj) { return obj->IsObject()?m_qobjectWrapper.isQObject(v8::Handle::Cast(obj)):false; } QObject *QV8Engine::toQObject(v8::Handle obj) { return obj->IsObject()?m_qobjectWrapper.toQObject(v8::Handle::Cast(obj)):0; } v8::Handle QV8Engine::newQObject(QObject *object) { return m_qobjectWrapper.newQObject(object); } v8::Handle QV8Engine::newQObject(QObject *object, const ObjectOwnership ownership) { if (!object) return v8::Null(); v8::Handle result = newQObject(object); QDeclarativeData *ddata = QDeclarativeData::get(object, true); if (ownership == JavaScriptOwnership && ddata) { ddata->indestructible = false; ddata->explicitIndestructibleSet = true; } return result; } v8::Local QV8Engine::toString(const QString &string) { return m_stringWrapper.toString(string); } v8::Handle QV8Engine::newValueType(QObject *object, int property, QDeclarativeValueType *type) { return m_valueTypeWrapper.newValueType(object, property, type); } v8::Handle QV8Engine::newValueType(const QVariant &value, QDeclarativeValueType *type) { return m_valueTypeWrapper.newValueType(value, type); } v8::Handle 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 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 // QDECLARATIVEV8ENGINE_P_H