From cb70f2096cbc66e6e8b5324e0e9508c788144e7e Mon Sep 17 00:00:00 2001
From: Aaron Kennedy <aaron.kennedy@nokia.com>
Date: Wed, 2 Dec 2009 12:35:38 +1000
Subject: Cache binding closures

---
 src/declarative/qml/qmlcompileddata.cpp       |  1 +
 src/declarative/qml/qmlcompiler.cpp           |  9 +++++
 src/declarative/qml/qmlcompiler_p.h           |  1 +
 src/declarative/qml/qmlcontextscriptclass.cpp | 33 ++++++++++++++--
 src/declarative/qml/qmlcontextscriptclass_p.h |  1 +
 src/declarative/qml/qmlengine.cpp             |  2 +-
 src/declarative/qml/qmlengine_p.h             |  2 +
 src/declarative/qml/qmlexpression.cpp         | 56 +++++++++++++++++++++------
 src/declarative/qml/qmlexpression_p.h         |  2 +
 src/declarative/qml/qmlrewrite.cpp            | 16 ++++++++
 src/declarative/qml/qmlrewrite_p.h            | 11 ++++++
 11 files changed, 118 insertions(+), 16 deletions(-)

diff --git a/src/declarative/qml/qmlcompileddata.cpp b/src/declarative/qml/qmlcompileddata.cpp
index 44d2745..3428574 100644
--- a/src/declarative/qml/qmlcompileddata.cpp
+++ b/src/declarative/qml/qmlcompileddata.cpp
@@ -168,6 +168,7 @@ QmlCompiledData::~QmlCompiledData()
         importCache->release();
 
     qDeleteAll(programs);
+    qDeleteAll(cachedValues);
 }
 
 QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt, const QBitField &bindings) const
diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp
index a4e14b2..a0e9418 100644
--- a/src/declarative/qml/qmlcompiler.cpp
+++ b/src/declarative/qml/qmlcompiler.cpp
@@ -2529,12 +2529,21 @@ bool QmlCompiler::completeComponentBuild()
 
             // Pre-rewrite the expression
             QString expression = binding.expression.asScript();
+
+            // ### Optimize
+            QmlRewrite::SharedBindingTester sharableTest;
+            bool isSharable = sharableTest.isSharable(expression);
+            
             QmlRewrite::RewriteBinding rewriteBinding;
             expression = rewriteBinding(expression);
 
             quint32 length = expression.length();
             quint32 pc = output->programs.length();
+            if (isSharable) 
+                pc |= 0x80000000;
+
             output->programs.append(0);
+            output->cachedValues.append(0);
             binding.compiledData =
                 QByteArray((const char *)&pc, sizeof(quint32)) +
                 QByteArray((const char *)&length, sizeof(quint32)) +
diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h
index b54a62a..ca26062 100644
--- a/src/declarative/qml/qmlcompiler_p.h
+++ b/src/declarative/qml/qmlcompiler_p.h
@@ -114,6 +114,7 @@ public:
     QList<QmlParser::Location> locations;
     QList<QmlInstruction> bytecode;
     QList<QScriptProgram *> programs;
+    QList<QScriptValue *> cachedValues;
     QList<QmlPropertyCache *> propertyCaches;
     QList<QmlIntegerCache *> contextCaches;
     QList<QmlParser::Object::ScriptBlock> scripts;
diff --git a/src/declarative/qml/qmlcontextscriptclass.cpp b/src/declarative/qml/qmlcontextscriptclass.cpp
index fda284a..a95400a 100644
--- a/src/declarative/qml/qmlcontextscriptclass.cpp
+++ b/src/declarative/qml/qmlcontextscriptclass.cpp
@@ -47,9 +47,27 @@
 QT_BEGIN_NAMESPACE
 
 struct ContextData : public QScriptDeclarativeClass::Object {
-    ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o) {}
+    ContextData() : isSharedContext(true) {}
+    ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o), isSharedContext(false) {}
     QGuard<QmlContext> context;
     QGuard<QObject> scopeObject;
+    bool isSharedContext;
+
+    QmlContext *getContext(QmlEngine *engine) {
+        if (isSharedContext) {
+            return QmlEnginePrivate::get(engine)->sharedContext;
+        } else {
+            return context.data();
+        }
+    }
+
+    QObject *getScope(QmlEngine *engine) {
+        if (isSharedContext) {
+            return QmlEnginePrivate::get(engine)->sharedScope;
+        } else {
+            return scopeObject.data();
+        }
+    }
 };
 
 /*
@@ -73,13 +91,20 @@ QScriptValue QmlContextScriptClass::newContext(QmlContext *context, QObject *sco
     return newObject(scriptEngine, this, new ContextData(context, scopeObject));
 }
 
+QScriptValue QmlContextScriptClass::newSharedContext()
+{
+    QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine);
+
+    return newObject(scriptEngine, this, new ContextData());
+}
+
 QmlContext *QmlContextScriptClass::contextFromValue(const QScriptValue &v)
 {
     if (scriptClass(v) != this)
         return 0;
 
     ContextData *data = (ContextData *)object(v);
-    return data->context;
+    return data->getContext(engine);
 }
 
 QScriptClass::QueryFlags 
@@ -94,8 +119,8 @@ QmlContextScriptClass::queryProperty(Object *object, const Identifier &name,
     lastPropertyIndex = -1;
     lastDefaultObject = -1;
 
-    QmlContext *bindContext = ((ContextData *)object)->context.data();
-    QObject *scopeObject = ((ContextData *)object)->scopeObject.data();
+    QmlContext *bindContext = ((ContextData *)object)->getContext(engine);
+    QObject *scopeObject = ((ContextData *)object)->getScope(engine);
     if (!bindContext)
         return 0;
 
diff --git a/src/declarative/qml/qmlcontextscriptclass_p.h b/src/declarative/qml/qmlcontextscriptclass_p.h
index bbd779b..10b848a 100644
--- a/src/declarative/qml/qmlcontextscriptclass_p.h
+++ b/src/declarative/qml/qmlcontextscriptclass_p.h
@@ -68,6 +68,7 @@ public:
     ~QmlContextScriptClass();
 
     QScriptValue newContext(QmlContext *, QObject * = 0);
+    QScriptValue newSharedContext();
 
     QmlContext *contextFromValue(const QScriptValue &);
 
diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp
index 9afb81e..0e24fd2 100644
--- a/src/declarative/qml/qmlengine.cpp
+++ b/src/declarative/qml/qmlengine.cpp
@@ -110,7 +110,7 @@ struct StaticQtMetaObject : public QObject
 
 QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e)
 : rootContext(0), currentExpression(0),
-  isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0),
+  isDebugging(false), contextClass(0), sharedContext(0), sharedScope(0), objectClass(0), valueTypeClass(0), globalClass(0),
   nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), cleanup(0), erroredBindings(0), 
   inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0), 
   rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1)
diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h
index 9e0c1fc..4eb9843 100644
--- a/src/declarative/qml/qmlengine_p.h
+++ b/src/declarative/qml/qmlengine_p.h
@@ -126,6 +126,8 @@ public:
 
     struct ImportedNamespace;
     QmlContextScriptClass *contextClass;
+    QmlContext *sharedContext;
+    QObject *sharedScope;
     QmlObjectScriptClass *objectClass;
     QmlValueTypeScriptClass *valueTypeClass;
     QmlTypeNameScriptClass *typeNameClass;
diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp
index d2d60ee..703fcf5 100644
--- a/src/declarative/qml/qmlexpression.cpp
+++ b/src/declarative/qml/qmlexpression.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
 
 QmlExpressionData::QmlExpressionData()
 : expressionFunctionValid(false), expressionRewritten(false), me(0), 
-  trackChange(true), line(-1), guardList(0), guardListLength(0)
+  trackChange(true), isShared(false), line(-1), guardList(0), guardListLength(0)
 {
 }
 
@@ -108,27 +108,47 @@ void QmlExpressionPrivate::init(QmlContext *ctxt, void *expr, QmlRefCount *rc,
         data->expression = QString::fromRawData((QChar *)(exprData + 3), exprData[2]);
 
         int progIdx = *(exprData + 1);
+        bool isShared = progIdx & 0x80000000;
+        progIdx &= 0x7FFFFFFF;
+
         QmlEngine *engine = ctxt->engine();
         QmlEnginePrivate *ep = QmlEnginePrivate::get(engine);
         QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine);
+
+        if (isShared) {
+
+            if (!dd->cachedValues.at(progIdx)) {
+                QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
+                scriptContext->pushScope(ep->contextClass->newSharedContext());
+                dd->cachedValues[progIdx] = new QScriptValue(scriptEngine->evaluate(data->expression, data->url.toString(), data->line));
+                scriptEngine->popContext();
+            }
+
+            data->expressionFunction = *dd->cachedValues.at(progIdx);
+            data->isShared = true;
+            data->expressionFunctionValid = true;
+
+        } else {
+
 #if !defined(Q_OS_SYMBIAN) //XXX Why doesn't this work?
-        if (!dd->programs.at(progIdx)) {
-            dd->programs[progIdx] =
-                new QScriptProgram(data->expression, data->url.toString(), data->line);
-        }
+            if (!dd->programs.at(progIdx)) {
+                dd->programs[progIdx] =
+                    new QScriptProgram(data->expression, data->url.toString(), data->line);
+            }
 #endif
 
-        QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
-        scriptContext->pushScope(ep->contextClass->newContext(ctxt, me));
+            QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
+            scriptContext->pushScope(ep->contextClass->newContext(ctxt, me));
 
 #if !defined(Q_OS_SYMBIAN) 
-        data->expressionFunction = scriptEngine->evaluate(*dd->programs[progIdx]);
+            data->expressionFunction = scriptEngine->evaluate(*dd->programs[progIdx]);
 #else
-        data->expressionFunction = scriptEngine->evaluate(data->expression);
+            data->expressionFunction = scriptEngine->evaluate(data->expression);
 #endif
 
-        data->expressionFunctionValid = true;
-        scriptEngine->popContext();
+            data->expressionFunctionValid = true;
+            scriptEngine->popContext();
+        }
     }
 
     data->QmlAbstractExpression::setContext(ctxt);
@@ -328,8 +348,22 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope, bool *isUnd
         data->expressionFunctionValid = true;
     }
 
+    QmlContext *oldSharedContext = 0;
+    QObject *oldSharedScope = 0;
+    if (data->isShared) {
+        oldSharedContext = ep->sharedContext;
+        oldSharedScope = ep->sharedScope;
+        ep->sharedContext = data->context();
+        ep->sharedScope = data->me;
+    }
+
     QScriptValue svalue = data->expressionFunction.call();
 
+    if (data->isShared) {
+        ep->sharedContext = oldSharedContext;
+        ep->sharedScope = oldSharedScope;
+    }
+
     if (isUndefined)
         *isUndefined = svalue.isUndefined() || scriptEngine->hasUncaughtException();
 
diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h
index c6ba54d..dbb12ba 100644
--- a/src/declarative/qml/qmlexpression_p.h
+++ b/src/declarative/qml/qmlexpression_p.h
@@ -101,6 +101,8 @@ public:
     QObject *me;
     bool trackChange;
 
+    bool isShared;
+
     QUrl url;
     int line;
 
diff --git a/src/declarative/qml/qmlrewrite.cpp b/src/declarative/qml/qmlrewrite.cpp
index 32e2fef..2964a75 100644
--- a/src/declarative/qml/qmlrewrite.cpp
+++ b/src/declarative/qml/qmlrewrite.cpp
@@ -49,6 +49,22 @@ DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP);
 
 namespace QmlRewrite {
 
+bool SharedBindingTester::isSharable(const QString &code)
+{
+    Engine engine;
+    NodePool pool(QString(), &engine);
+    Lexer lexer(&engine);
+    Parser parser(&engine);
+    lexer.setCode(code, 0);
+    parser.parseStatement();
+    if (!parser.statement()) 
+        return false;
+
+    _sharable = true;
+    AST::Node::acceptChild(parser.statement(), this);
+    return _sharable;
+}
+
 QString RewriteBinding::operator()(const QString &code, bool *ok)
 {
     Engine engine;
diff --git a/src/declarative/qml/qmlrewrite_p.h b/src/declarative/qml/qmlrewrite_p.h
index a5cb841..a04a0db 100644
--- a/src/declarative/qml/qmlrewrite_p.h
+++ b/src/declarative/qml/qmlrewrite_p.h
@@ -63,6 +63,17 @@ QT_BEGIN_NAMESPACE
 namespace QmlRewrite {
 using namespace QmlJS;
 
+class SharedBindingTester : protected AST::Visitor
+{
+    bool _sharable;
+public:
+    bool isSharable(const QString &code);
+    
+    virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; }
+    virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; }
+    virtual bool visit(AST::CallExpression *) { _sharable = false; return false; }
+};
+
 class RewriteBinding: protected AST::Visitor
 {
     unsigned _position;
-- 
cgit v0.12