Optimized QDeclarativeScriptString constructor for QDeclarativeExpression.
authorMichael Brasser <michael.brasser@nokia.com>
Wed, 8 Jun 2011 03:18:35 +0000 (13:18 +1000)
committerMichael Brasser <michael.brasser@nokia.com>
Thu, 9 Jun 2011 00:10:09 +0000 (10:10 +1000)
When the compiler sees a script string, it will attempt to rewrite it
and store the id for the rewrite in the script string. We can then
create a QDeclarativeExpression using the id, which saves us a rewrite
at runtime.

Reviewed-by: Aaron Kennedy
14 files changed:
doc/src/declarative/whatsnew.qdoc
src/declarative/qml/qdeclarativecompiler.cpp
src/declarative/qml/qdeclarativeexpression.cpp
src/declarative/qml/qdeclarativeexpression.h
src/declarative/qml/qdeclarativeinstruction.cpp
src/declarative/qml/qdeclarativeinstruction_p.h
src/declarative/qml/qdeclarativescriptstring.cpp
src/declarative/qml/qdeclarativescriptstring.h
src/declarative/qml/qdeclarativescriptstring_p.h [new file with mode: 0644]
src/declarative/qml/qdeclarativevme.cpp
src/declarative/qml/qml.pri
tests/auto/declarative/qdeclarativeexpression/data/scriptString.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeexpression/qdeclarativeexpression.pro [new file with mode: 0644]
tests/auto/declarative/qdeclarativeexpression/tst_qdeclarativeexpression.cpp [new file with mode: 0644]

index bcf27e1..2ce16ee 100644 (file)
@@ -40,6 +40,9 @@ You can still ignore these events in the handler to let them pass through.
 The Binding element now restores any previously set binding when its \e when
 clause becomes false.
 
+QDeclarativeExpression can now be directly (and more efficiently) constructed from a
+QDeclarativeScriptString.
+
 \section1 Qt 4.7.4 includes QtQuick 1.1
 
 QtQuick 1.1 is a minor feature update.  \e {import QtQuick 1.1} to use the new
index e66a3fe..72010c0 100644 (file)
@@ -1012,12 +1012,14 @@ void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj)
 {
     typedef QPair<Property *, int> PropPair;
     foreach(const PropPair &prop, obj->scriptStringProperties) {
+        const QString &script = prop.first->values.at(0)->value.asScript();
         QDeclarativeInstruction ss;
         ss.setType(QDeclarativeInstruction::StoreScriptString);
         ss.storeScriptString.propertyIndex = prop.first->index;
-        ss.storeScriptString.value = 
-            output->indexForString(prop.first->values.at(0)->value.asScript());
+        ss.storeScriptString.value = output->indexForString(script);
         ss.storeScriptString.scope = prop.second;
+        ss.storeScriptString.bindingId = rewriteBinding(script, prop.first->name);
+        ss.storeScriptString.line = prop.first->location.start.line;
         output->addInstruction(ss);
     }
 
index 247bde9..8079cf3 100644 (file)
@@ -45,6 +45,7 @@
 #include "private/qdeclarativeengine_p.h"
 #include "private/qdeclarativecontext_p.h"
 #include "private/qdeclarativerewrite_p.h"
+#include "private/qdeclarativescriptstring_p.h"
 #include "private/qdeclarativecompiler_p.h"
 #include "private/qdeclarativeglobalscriptclass_p.h"
 
@@ -271,6 +272,58 @@ QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, vo
 /*!
     Create a QDeclarativeExpression object that is a child of \a parent.
 
+    The \script provides the expression to be evaluated, the context to evaluate it in,
+    and the scope object to evaluate it with.
+
+    This constructor is functionally equivalent to the following, but in most cases
+    is more efficient.
+    \code
+    QDeclarativeExpression expression(script.context(), script.scopeObject(), script.script(), parent);
+    \endcode
+
+    \sa QDeclarativeScriptString
+*/
+QDeclarativeExpression::QDeclarativeExpression(const QDeclarativeScriptString &script, QObject *parent)
+: QObject(*new QDeclarativeExpressionPrivate, parent)
+{
+    Q_D(QDeclarativeExpression);
+    bool defaultConstruction = false;
+
+    int id = script.d.data()->bindingId;
+    if (id < 0) {
+        defaultConstruction = true;
+    } else {
+        QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(script.context());
+
+        QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(qmlEngine(script.scopeObject()));
+        QDeclarativeCompiledData *cdata = 0;
+        QDeclarativeTypeData *typeData = 0;
+        if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
+            typeData = engine->typeLoader.get(ctxtdata->url);
+            cdata = typeData->compiledData();
+        }
+
+        if (cdata)
+            d->init(ctxtdata, (void*)cdata->datas.at(id).constData(), cdata, script.scopeObject(),
+                    cdata->name, script.d.data()->lineNumber);
+        else
+           defaultConstruction = true;
+
+        if (typeData)
+            typeData->release();
+    }
+
+    if (defaultConstruction)
+        d->init(QDeclarativeContextData::get(script.context()), script.script(), script.scopeObject());
+
+    if (QDeclarativeExpression_notifyIdx == -1)
+        QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
+    d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
+}
+
+/*!
+    Create a QDeclarativeExpression object that is a child of \a parent.
+
     The \a expression JavaScript will be executed in the \a ctxt QDeclarativeContext.
     If specified, the \a scope object's properties will also be in scope during
     the expression's execution.
index fef97de..b76205c 100644 (file)
@@ -43,6 +43,7 @@
 #define QDECLARATIVEEXPRESSION_H
 
 #include <QtDeclarative/qdeclarativeerror.h>
+#include <QtDeclarative/qdeclarativescriptstring.h>
 
 #include <QtCore/qobject.h>
 #include <QtCore/qvariant.h>
@@ -66,6 +67,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeExpression : public QObject
 public:
     QDeclarativeExpression();
     QDeclarativeExpression(QDeclarativeContext *, QObject *, const QString &, QObject * = 0);
+    explicit QDeclarativeExpression(const QDeclarativeScriptString &, QObject * = 0);
     virtual ~QDeclarativeExpression();
 
     QDeclarativeEngine *engine() const;
index fa0d8ea..79a2a1c 100644 (file)
@@ -160,7 +160,7 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx)
         qWarning().nospace() << idx << "\t\t" << "STORE_IMPORTED_SCRIPT\t" << instr->storeScript.value;
         break;
     case QDeclarativeInstruction::StoreScriptString:
-        qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope;
+        qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope << "\t" << instr->storeScriptString.bindingId;
         break;
     case QDeclarativeInstruction::AssignSignalObject:
         qWarning().nospace() << idx << "\t\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal);
index d040967..3db55a6 100644 (file)
@@ -245,6 +245,8 @@ union QDeclarativeInstruction
         int propertyIndex;
         int value;
         int scope;
+        int bindingId;
+        ushort line;
     }; 
     struct instr_storeScript {
         QML_INSTR_HEADER
index f544393..02d6e56 100644 (file)
 ****************************************************************************/
 
 #include "qdeclarativescriptstring.h"
+#include "qdeclarativescriptstring_p.h"
 
 QT_BEGIN_NAMESPACE
 
-class QDeclarativeScriptStringPrivate : public QSharedData
-{
-public:
-    QDeclarativeScriptStringPrivate() : context(0), scope(0) {}
-
-    QDeclarativeContext *context;
-    QObject *scope;
-    QString script;
-};
-
 /*!
 \class QDeclarativeScriptString
 \since 4.7
@@ -75,8 +66,8 @@ and the class could choose how to handle it. Typically, the class will evaluate
 the script at some later time using a QDeclarativeExpression.
 
 \code
-QDeclarativeExpression expr(scriptString.context(), scriptString.script(), scriptStr.scopeObject());
-expr.value();
+QDeclarativeExpression expr(scriptString);
+expr.evaluate();
 \endcode
 
 \sa QDeclarativeExpression
index 5408dd4..5f3a2fe 100644 (file)
@@ -75,6 +75,9 @@ public:
 
 private:
     QSharedDataPointer<QDeclarativeScriptStringPrivate> d;
+
+    friend class QDeclarativeVME;
+    friend class QDeclarativeExpression;
 };
 
 QT_END_NAMESPACE
diff --git a/src/declarative/qml/qdeclarativescriptstring_p.h b/src/declarative/qml/qdeclarativescriptstring_p.h
new file mode 100644 (file)
index 0000000..cd0cc43
--- /dev/null
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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$
+** 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 QDECLARATIVESCRIPTSTRING_P_H
+#define QDECLARATIVESCRIPTSTRING_P_H
+
+#include <QtDeclarative/qdeclarativecontext.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeScriptStringPrivate : public QSharedData
+{
+public:
+    QDeclarativeScriptStringPrivate() : context(0), scope(0), bindingId(-1), lineNumber(-1) {}
+
+    QDeclarativeContext *context;
+    QObject *scope;
+    QString script;
+    int bindingId;
+    int lineNumber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVESCRIPTSTRING_P_H
index dd080a8..a9b303c 100644 (file)
@@ -61,6 +61,7 @@
 #include "private/qdeclarativeglobal_p.h"
 #include "private/qdeclarativeglobalscriptclass_p.h"
 #include "qdeclarativescriptstring.h"
+#include "qdeclarativescriptstring_p.h"
 
 #include <QStack>
 #include <QWidget>
@@ -640,6 +641,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack,
             ss.setContext(ctxt->asQDeclarativeContext());
             ss.setScopeObject(scope);
             ss.setScript(primitives.at(instr.value));
+            ss.d.data()->bindingId = instr.bindingId;
+            ss.d.data()->lineNumber = instr.line;
 
             void *a[] = { &ss, 0, &status, &flags };
             QMetaObject::metacall(target, QMetaObject::WriteProperty, 
index 2333590..5d56b30 100644 (file)
@@ -129,6 +129,7 @@ HEADERS += \
     $$PWD/qdeclarativeimport_p.h \
     $$PWD/qdeclarativeextensionplugin.h \
     $$PWD/qintrusivelist_p.h \
+    $$PWD/qdeclarativescriptstring_p.h
 
 QT += sql
 include(parser/parser.pri)
diff --git a/tests/auto/declarative/qdeclarativeexpression/data/scriptString.qml b/tests/auto/declarative/qdeclarativeexpression/data/scriptString.qml
new file mode 100644 (file)
index 0000000..edd4048
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQuick 1.0
+import Test 1.0
+
+TestObject {
+    property int value1: 10
+    property int value2: 5
+    scriptString: value1 + value2
+    scriptStringError: value3 * 5
+}
diff --git a/tests/auto/declarative/qdeclarativeexpression/qdeclarativeexpression.pro b/tests/auto/declarative/qdeclarativeexpression/qdeclarativeexpression.pro
new file mode 100644 (file)
index 0000000..8c73a7c
--- /dev/null
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qdeclarativeexpression.cpp
+
+symbian: {
+    importFiles.files = data
+    importFiles.path = .
+    DEPLOYMENT += importFiles
+} else {
+    DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
+QT += core-private gui-private declarative-private
diff --git a/tests/auto/declarative/qdeclarativeexpression/tst_qdeclarativeexpression.cpp b/tests/auto/declarative/qdeclarativeexpression/tst_qdeclarativeexpression.cpp
new file mode 100644 (file)
index 0000000..aa8c12e
--- /dev/null
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qtest.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativescriptstring.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_qdeclarativeexpression : public QObject
+{
+    Q_OBJECT
+public:
+    tst_qdeclarativeexpression() {}
+
+private slots:
+    void scriptString();
+};
+
+class TestObject : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QDeclarativeScriptString scriptString READ scriptString WRITE setScriptString)
+    Q_PROPERTY(QDeclarativeScriptString scriptStringError READ scriptStringError WRITE setScriptStringError)
+public:
+    TestObject(QObject *parent = 0) : QObject(parent) {}
+
+    QDeclarativeScriptString scriptString() const { return m_scriptString; }
+    void setScriptString(QDeclarativeScriptString scriptString) { m_scriptString = scriptString; }
+
+    QDeclarativeScriptString scriptStringError() const { return m_scriptStringError; }
+    void setScriptStringError(QDeclarativeScriptString scriptString) { m_scriptStringError = scriptString; }
+
+private:
+    QDeclarativeScriptString m_scriptString;
+    QDeclarativeScriptString m_scriptStringError;
+};
+
+QML_DECLARE_TYPE(TestObject)
+
+void tst_qdeclarativeexpression::scriptString()
+{
+    qmlRegisterType<TestObject>("Test", 1, 0, "TestObject");
+
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/scriptString.qml"));
+    TestObject *testObj = qobject_cast<TestObject*>(c.create());
+    QVERIFY(testObj != 0);
+
+    QDeclarativeScriptString script = testObj->scriptString();
+    QCOMPARE(script.script(), QLatin1String("value1 + value2"));
+
+    QDeclarativeExpression expression(script);
+    QVariant value = expression.evaluate();
+    QCOMPARE(value.toInt(), 15);
+
+    QDeclarativeScriptString scriptError = testObj->scriptStringError();
+    QCOMPARE(scriptError.script(), QLatin1String("value3 * 5"));
+
+    //verify that the expression has the correct error location information
+    QDeclarativeExpression expressionError(scriptError);
+    QVariant valueError = expressionError.evaluate();
+    QVERIFY(!valueError.isValid());
+    QVERIFY(expressionError.hasError());
+    QDeclarativeError error = expressionError.error();
+    QCOMPARE(error.url(), c.url());
+    QCOMPARE(error.line(), 8);
+}
+
+QTEST_MAIN(tst_qdeclarativeexpression)
+
+#include "tst_qdeclarativeexpression.moc"