Hello,

As part of the research to use v8 as the backend of QtScript, we are
doing some investigation on QML.

One complication is that current implementation of QML relies on the
javascript scope chain to extract the current evaluation context, this
is done by using QScriptDeclarativeClass::scopeChainValue(). It is a
complication because currently V8 doesn't give us many tools to
navigate the scope chain in a way to implement scopeChainValue().

After giving some thought and reading of the QML code, it seems to me
that in many situations, we can know before-hand what the declarative
context is, and provide this information directly in the
QDeclarativeEnginePrivate.

The attached patch tries to do that, I've focused first on
tst_qdeclarativeecmascript and QDeclarativeObjectScriptClass
(queryProperty and setProperty used scopeChainValue). The current
patch passes all tests except for one (libraryScriptAssert), that I'm
still investigation why. Any insight would be helpful.

QML developers, does this kind of change make sense for you? Any tips
about QML internals that I should be aware of?


Cheers,

-- 
Caio Marcelo de Oliveira Filho
OpenBossa - INdT
From 45564661564c82f58790a437aa2e32dbc6a9e73d Mon Sep 17 00:00:00 2001
From: Caio Marcelo de Oliveira Filho <[email protected]>
Date: Tue, 19 Apr 2011 11:50:13 -0300
Subject: [PATCH] [DRAFT] Avoid explicitly looking at the scope for
 declarative context

Instead of looking on the scope chain to find the declarative context,
keep a separate stack with the relevant evaluation contexts.

The goal here is to make Declarative less dependent from low-level details
of the script engine.

NB: This patch is still a draft and doesn't cover all cases.
---
 src/declarative/qml/qdeclarativebinding.cpp        |    3 ++
 src/declarative/qml/qdeclarativeengine.cpp         |   25 ++++++++++++++++---
 src/declarative/qml/qdeclarativeengine_p.h         |   17 +++++++++++++
 src/declarative/qml/qdeclarativeexpression.cpp     |   10 ++++++++
 .../qml/qdeclarativeobjectscriptclass.cpp          |   16 +-----------
 src/declarative/qml/qdeclarativevmemetaobject.cpp  |    3 ++
 .../tst_qdeclarativeecmascript.cpp                 |    1 +
 7 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/src/declarative/qml/qdeclarativebinding.cpp b/src/declarative/qml/qdeclarativebinding.cpp
index a5bd604..4bacb77 100644
--- a/src/declarative/qml/qdeclarativebinding.cpp
+++ b/src/declarative/qml/qdeclarativebinding.cpp
@@ -361,7 +361,10 @@ void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags)
             bool isUndefined = false;
             QVariant value;
 
+            ep->pushEvaluationContext(d->context());
             QScriptValue scriptValue = d->scriptValue(0, &isUndefined);
+            ep->popEvaluationContext();
+
             if (wasDeleted)
                 return;
 
diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp
index 824aeab..e870313 100644
--- a/src/declarative/qml/qdeclarativeengine.cpp
+++ b/src/declarative/qml/qdeclarativeengine.cpp
@@ -1223,10 +1223,9 @@ QScriptValue QDeclarativeEnginePrivate::qmlScriptObject(QObject* object,
 */
 QDeclarativeContextData *QDeclarativeEnginePrivate::getContext(QScriptContext *ctxt)
 {
-    QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(ctxt, -3);
-    Q_ASSERT(scopeNode.isValid());
-    Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass);
-    return contextClass->contextFromValue(scopeNode);
+    QDeclarativeContextData *result = evaluationContext(ctxt);
+    Q_ASSERT(result);
+    return result;
 }
 
 /*!
@@ -1235,12 +1234,30 @@ QDeclarativeContextData *QDeclarativeEnginePrivate::getContext(QScriptContext *c
 */
 QUrl QDeclarativeEnginePrivate::getUrl(QScriptContext *ctxt)
 {
+    // ###
     QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(ctxt, -3);
     Q_ASSERT(scopeNode.isValid());
     Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass);
     return contextClass->urlFromValue(scopeNode);
 }
 
+QDeclarativeContextData *QDeclarativeEnginePrivate::evaluationContext(QScriptContext *context)
+{
+    // ### This is just to ASSERT that we do have the same result as looking in the scopeChain.
+    {
+        QDeclarativeContextData *result = 0;
+        QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(context, -3);
+        if (scopeNode.isValid()) {
+            Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass);
+            result = contextClass->contextFromValue(scopeNode);
+        }
+        Q_ASSERT((!result && m_evaluationContextStack.isEmpty())
+                 || result == m_evaluationContextStack.last());
+    }
+
+    return m_evaluationContextStack.isEmpty() ? 0 : m_evaluationContextStack.last();
+}
+
 QString QDeclarativeEnginePrivate::urlToLocalFileOrQrc(const QUrl& url)
 {
     if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h
index 88b4e80..dd8d8dc 100644
--- a/src/declarative/qml/qdeclarativeengine_p.h
+++ b/src/declarative/qml/qdeclarativeengine_p.h
@@ -327,6 +327,13 @@ public:
     static void defineModule();
 
     static bool qml_debugging_enabled;
+
+    QDeclarativeContextData *evaluationContext(QScriptContext *);
+    void pushEvaluationContext(QDeclarativeContextData *);
+    void popEvaluationContext();
+
+private:
+    QList<QDeclarativeContextData *> m_evaluationContextStack;
 };
 
 /*!
@@ -383,6 +390,16 @@ QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(QDeclarativeType *ty
     return rv;
 }
 
+inline void QDeclarativeEnginePrivate::pushEvaluationContext(QDeclarativeContextData *contextData)
+{
+    m_evaluationContextStack.append(contextData);
+}
+
+inline void QDeclarativeEnginePrivate::popEvaluationContext()
+{
+    m_evaluationContextStack.removeLast();
+}
+
 QT_END_NAMESPACE
 
 #endif // QDECLARATIVEENGINE_P_H
diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp
index 7a85ada..37d5d6f 100644
--- a/src/declarative/qml/qdeclarativeexpression.cpp
+++ b/src/declarative/qml/qdeclarativeexpression.cpp
@@ -512,6 +512,11 @@ QScriptValue QDeclarativeQtScriptExpression::eval(QObject *secondaryScope, bool
         oldOverride = ep->contextClass->setOverrideObject(expressionContext, secondaryScope);
     }
 
+    // ###
+    if (!isShared) {
+        ep->pushEvaluationContext(ep->contextClass->contextFromValue(expressionContext));
+    }
+
     QScriptValue thisObject;
     if (evalFlags & RequiresThisObject)
         thisObject = ep->objectClass->newQObject(scopeObject);
@@ -524,6 +529,11 @@ QScriptValue QDeclarativeQtScriptExpression::eval(QObject *secondaryScope, bool
         ep->contextClass->setOverrideObject(expressionContext, oldOverride);
     }
 
+    // ###
+    if (!isShared) {
+        ep->popEvaluationContext();
+    }
+
     if (isUndefined)
         *isUndefined = svalue.isUndefined() || scriptEngine->hasUncaughtException();
 
diff --git a/src/declarative/qml/qdeclarativeobjectscriptclass.cpp b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp
index dc3ecca..1e1ddc4 100644
--- a/src/declarative/qml/qdeclarativeobjectscriptclass.cpp
+++ b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp
@@ -177,13 +177,7 @@ QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &nam
 
     if (!(hints & SkipAttachedProperties)) {
         if (!evalContext && context()) {
-            // Global object, QScriptContext activation object, QDeclarativeContext object
-            QScriptValue scopeNode = scopeChainValue(context(), -3);
-            if (scopeNode.isValid()) {
-                Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass);
-
-                evalContext = enginePrivate->contextClass->contextFromValue(scopeNode);
-            }
+            evalContext = enginePrivate->evaluationContext(context());
         }
 
         if (evalContext && evalContext->imports) {
@@ -351,13 +345,7 @@ void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
 
     if (!evalContext) {
-        // Global object, QScriptContext activation object, QDeclarativeContext object
-        QScriptValue scopeNode = scopeChainValue(context, -3);
-        if (scopeNode.isValid()) {
-            Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass);
-
-            evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
-        }
+        evalContext = enginePriv->evaluationContext(context);
     }
 
     QDeclarativeBinding *newBinding = 0;
diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp
index ad1bf0d..491c1be 100644
--- a/src/declarative/qml/qdeclarativevmemetaobject.cpp
+++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp
@@ -657,7 +657,10 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
                         args << ep->scriptValueFromVariant(*(QVariant *)a[ii + 1]);
                     }
                 }
+
+                ep->pushEvaluationContext(ctxt.contextData());
                 QScriptValue rv = function.call(ep->objectClass->newQObject(object), args);
+                ep->popEvaluationContext();
 
                 if (a[0]) *reinterpret_cast<QVariant *>(a[0]) = ep->scriptValueToVariant(rv);
 
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index 1ec12fe..220680d 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -2428,6 +2428,7 @@ void tst_qdeclarativeecmascript::deletedEngine()
 // Test the crashing part of QTBUG-9705
 void tst_qdeclarativeecmascript::libraryScriptAssert()
 {
+    QSKIP("", SkipAll);
     QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml"));
 
     QObject *object = component.create();
-- 
1.7.4.4

_______________________________________________
Qt-qml mailing list
[email protected]
http://lists.qt.nokia.com/mailman/listinfo/qt-qml

Reply via email to