/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QQMLEXPRESSION_P_H #define QQMLEXPRESSION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qqmlexpression.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QQmlAbstractExpression { public: QQmlAbstractExpression(); virtual ~QQmlAbstractExpression(); bool isValid() const; QQmlContextData *context() const; void setContext(QQmlContextData *); virtual void refresh(); class DeleteWatcher { public: inline DeleteWatcher(QQmlAbstractExpression *); inline ~DeleteWatcher(); inline bool wasDeleted() const; private: friend class QQmlAbstractExpression; QQmlContextData *_c; QQmlAbstractExpression **_w; QQmlAbstractExpression *_s; }; private: friend class QQmlContext; friend class QQmlContextData; friend class QQmlContextPrivate; QBiPointer m_context; QQmlAbstractExpression **m_prevExpression; QQmlAbstractExpression *m_nextExpression; }; class QQmlDelayedError { public: inline QQmlDelayedError() : nextError(0), prevError(0) {} inline ~QQmlDelayedError() { removeError(); } QQmlError error; bool addError(QQmlEnginePrivate *); inline void removeError() { if (!prevError) return; if (nextError) nextError->prevError = prevError; *prevError = nextError; nextError = 0; prevError = 0; } private: QQmlDelayedError *nextError; QQmlDelayedError **prevError; }; class QQmlJavaScriptExpression { public: // Although this looks crazy, we implement our own "vtable" here, rather than relying on // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage // location that is use for the vtable to also store the rarely used delayed error. // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in // memory for every expression. struct VTable { QString (*expressionIdentifier)(QQmlJavaScriptExpression *); void (*expressionChanged)(QQmlJavaScriptExpression *); }; QQmlJavaScriptExpression(VTable *vtable); v8::Local evaluate(QQmlContextData *, v8::Handle, bool *isUndefined); inline bool requiresThisObject() const; inline void setRequiresThisObject(bool v); inline bool useSharedContext() const; inline void setUseSharedContext(bool v); inline bool notifyOnValueChanged() const; void setNotifyOnValueChanged(bool v); void resetNotifyOnValueChanged(); inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); class DeleteWatcher { public: inline DeleteWatcher(QQmlJavaScriptExpression *); inline ~DeleteWatcher(); inline bool wasDeleted() const; private: friend class QQmlJavaScriptExpression; QObject *_c; QQmlJavaScriptExpression **_w; QQmlJavaScriptExpression *_s; }; inline bool hasError() const; inline bool hasDelayedError() const; QQmlError error() const; void clearError(); QQmlDelayedError *delayedError(); protected: ~QQmlJavaScriptExpression(); private: typedef QQmlJavaScriptExpressionGuard Guard; friend class QQmlJavaScriptExpressionGuard; struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e) : engine(engine), expression(e), errorString(0) { } ~GuardCapture() { Q_ASSERT(guards.isEmpty()); Q_ASSERT(errorString == 0); } virtual void captureProperty(QQmlNotifier *); virtual void captureProperty(QObject *, int, int); QQmlEngine *engine; QQmlJavaScriptExpression *expression; QFieldList guards; QStringList *errorString; }; QPointerValuePair m_vtable; // We store some flag bits in the following flag pointers. // m_scopeObject:flag1 - requiresThisObject // activeGuards:flag1 - notifyOnValueChanged // activeGuards:flag2 - useSharedContext QBiPointer m_scopeObject; QForwardFieldList activeGuards; void clearGuards(); }; class QQmlExpression; class QString; class Q_QML_PRIVATE_EXPORT QQmlExpressionPrivate : public QObjectPrivate, public QQmlJavaScriptExpression, public QQmlAbstractExpression { Q_DECLARE_PUBLIC(QQmlExpression) public: QQmlExpressionPrivate(); ~QQmlExpressionPrivate(); void init(QQmlContextData *, const QString &, QObject *); void init(QQmlContextData *, v8::Handle, QObject *); void init(QQmlContextData *, const QString &, bool, QObject *, const QString &, int, int); void init(QQmlContextData *, const QByteArray &, bool, QObject *, const QString &, int, int); QVariant value(QObject *secondaryScope = 0, bool *isUndefined = 0); v8::Local v8value(QObject *secondaryScope = 0, bool *isUndefined = 0); static inline QQmlExpressionPrivate *get(QQmlExpression *expr); static inline QQmlExpression *get(QQmlExpressionPrivate *expr); void _q_notify(); static void exceptionToError(v8::Handle, QQmlError &); static v8::Persistent evalFunction(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, int line, v8::Persistent *qmlscope = 0); static v8::Persistent evalFunction(QQmlContextData *ctxt, QObject *scope, const char *code, int codeLength, const QString &filename, int line, v8::Persistent *qmlscope = 0); static QQmlExpression *create(QQmlContextData *, QObject *, const QString &, bool, const QString &, int, int); bool expressionFunctionValid:1; bool expressionFunctionRewritten:1; bool extractExpressionFromFunction:1; // "Inherited" from QQmlJavaScriptExpression static QString expressionIdentifier(QQmlJavaScriptExpression *); static void expressionChanged(QQmlJavaScriptExpression *); virtual void expressionChanged(); QString expression; QByteArray expressionUtf8; v8::Persistent v8qmlscope; v8::Persistent v8function; QString url; // This is a QString for a reason. QUrls are slooooooow... int line; int column; QString name; //function name, hint for the debugger QQmlRefCount *dataRef; }; QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) : _c(0), _w(0), _s(e) { if (e->m_context.isT1()) { _w = &_s; _c = e->m_context.asT1(); e->m_context = this; } else { // Another watcher is already registered _w = &e->m_context.asT2()->_s; } } QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() { Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); if (*_w && _s->m_context.asT2() == this) _s->m_context = _c; } bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const { return *_w == 0; } QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) : _c(0), _w(0), _s(e) { if (e->m_scopeObject.isT1()) { _w = &_s; _c = e->m_scopeObject.asT1(); e->m_scopeObject = this; } else { // Another watcher is already registered _w = &e->m_scopeObject.asT2()->_s; } } QQmlJavaScriptExpression::DeleteWatcher::~DeleteWatcher() { Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_scopeObject.isT2())); if (*_w && _s->m_scopeObject.asT2() == this) _s->m_scopeObject = _c; } bool QQmlJavaScriptExpression::DeleteWatcher::wasDeleted() const { return *_w == 0; } bool QQmlJavaScriptExpression::requiresThisObject() const { return m_scopeObject.flag(); } void QQmlJavaScriptExpression::setRequiresThisObject(bool v) { m_scopeObject.setFlagValue(v); } bool QQmlJavaScriptExpression::useSharedContext() const { return activeGuards.flag2(); } void QQmlJavaScriptExpression::setUseSharedContext(bool v) { activeGuards.setFlag2Value(v); } bool QQmlJavaScriptExpression::notifyOnValueChanged() const { return activeGuards.flag(); } QObject *QQmlJavaScriptExpression::scopeObject() const { if (m_scopeObject.isT1()) return m_scopeObject.asT1(); else return m_scopeObject.asT2()->_c; } void QQmlJavaScriptExpression::setScopeObject(QObject *v) { if (m_scopeObject.isT1()) m_scopeObject = v; else m_scopeObject.asT2()->_c = v; } bool QQmlJavaScriptExpression::hasError() const { return m_vtable.hasValue() && m_vtable.constValue()->error.isValid(); } bool QQmlJavaScriptExpression::hasDelayedError() const { return m_vtable.hasValue(); } QQmlExpressionPrivate *QQmlExpressionPrivate::get(QQmlExpression *expr) { return static_cast(QObjectPrivate::get(expr)); } QQmlExpression *QQmlExpressionPrivate::get(QQmlExpressionPrivate *expr) { return expr->q_func(); } QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) : expression(e), next(0) { callback = &endpointCallback; } void QQmlJavaScriptExpressionGuard::endpointCallback(QQmlNotifierEndpoint *e) { QQmlJavaScriptExpression *expression = static_cast(e)->expression; expression->m_vtable->expressionChanged(expression); } QQmlJavaScriptExpressionGuard * QQmlJavaScriptExpressionGuard::New(QQmlJavaScriptExpression *e, QQmlEngine *engine) { Q_ASSERT(e); return QQmlEnginePrivate::get(engine)->jsExpressionGuardPool.New(e); } void QQmlJavaScriptExpressionGuard::Delete() { QRecyclePool::Delete(this); } QT_END_NAMESPACE #endif // QQMLEXPRESSION_P_H