From 1855316aaae722ed53ba68e17f4328471d882513 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 15 Jan 2010 17:30:26 +0100
Subject: [PATCH] Script: Some refactoring.

---
 .../scriptmanager/scriptmanager.cpp           |  25 +--
 .../coreplugin/scriptmanager/scriptmanager.h  |  10 +-
 .../scriptmanager/scriptmanager_p.h           |   7 +-
 src/plugins/debugger/script/scriptengine.cpp  | 202 +++++++++++-------
 src/plugins/debugger/script/scriptengine.h    |  12 +-
 5 files changed, 162 insertions(+), 94 deletions(-)

diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp b/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp
index 1d13fa5ad38..8d7761b7285 100644
--- a/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp
+++ b/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp
@@ -231,15 +231,15 @@ bool ScriptManagerPrivate::runScript(const QString &script, QString *errorMessag
     return !failed;
 }
 
-void ScriptManagerPrivate::ensureEngineInitialized()
+ScriptManager::QScriptEnginePtr ScriptManagerPrivate::ensureEngineInitialized()
 {
-    if (m_engine)
-        return;
-    m_engine = new QScriptEngine(this);
+    if (!m_engine.isNull())
+        return m_engine;
+    m_engine = QScriptEnginePtr(new QScriptEngine(this));
     // register QObjects that occur as properties
-    SharedTools::registerQObject<QMainWindow>(m_engine);
-    SharedTools::registerQObject<QStatusBar>(m_engine);
-    SharedTools::registerQObject<QSettings>(m_engine);
+    SharedTools::registerQObject<QMainWindow>(m_engine.data());
+    SharedTools::registerQObject<QStatusBar>(m_engine.data());
+    SharedTools::registerQObject<QSettings>(m_engine.data());
     // WB interfaces
 //    SharedTools::registerQObjectInterface<Core::MessageManager, MessageManagerPrototype>(m_engine);
 
@@ -248,17 +248,17 @@ void ScriptManagerPrivate::ensureEngineInitialized()
 //    SharedTools::registerQObjectInterface<Core::FileManager, FileManagerPrototype>(m_engine);
 
 //    SharedTools::registerQObjectInterface<Core::IEditor, EditorPrototype>(m_engine);
-    qScriptRegisterSequenceMetaType<QList<Core::IEditor *> >(m_engine);
+    qScriptRegisterSequenceMetaType<QList<Core::IEditor *> >(m_engine.data());
 
 //    SharedTools::registerQObjectInterface<Core::EditorGroup, EditorGroupPrototype>(m_engine);
-    qScriptRegisterSequenceMetaType<QList<Core::EditorGroup *> >(m_engine);
+    qScriptRegisterSequenceMetaType<QList<Core::EditorGroup *> >(m_engine.data());
 
-    SharedTools::registerQObjectInterface<Core::EditorManager, EditorManagerPrototype>(m_engine);
+    SharedTools::registerQObjectInterface<Core::EditorManager, EditorManagerPrototype>(m_engine.data());
 
 //    SharedTools::registerQObjectInterface<Core::ICore, CorePrototype>(m_engine);
 
     // Make "core" available
-    m_engine->globalObject().setProperty(QLatin1String("core"), qScriptValueFromValue(m_engine, Core::ICore::instance()));
+    m_engine->globalObject().setProperty(QLatin1String("core"), qScriptValueFromValue(m_engine.data(), Core::ICore::instance()));
 
     // CLASSIC:  registerInterfaceWithDefaultPrototype<Core::MessageManager, MessageManagerPrototype>(m_engine);
 
@@ -284,9 +284,10 @@ void ScriptManagerPrivate::ensureEngineInitialized()
     m_engine->globalObject().setProperty(QLatin1String("getOpenFileName"), m_engine->newFunction(fileBox<QFileDialog::AcceptOpen, QFileDialog::ExistingFile> , 2));
     m_engine->globalObject().setProperty(QLatin1String("getSaveFileName"), m_engine->newFunction(fileBox<QFileDialog::AcceptSave, QFileDialog::AnyFile> , 2));
     m_engine->globalObject().setProperty(QLatin1String("getExistingDirectory"), m_engine->newFunction(fileBox<QFileDialog::AcceptSave, QFileDialog::DirectoryOnly> , 2));
+    return m_engine;
 }
 
-QString ScriptManagerPrivate::engineError(QScriptEngine *scriptEngine)
+QString ScriptManagerPrivate::engineError(const QScriptEnginePtr &scriptEngine)
 {
     QScriptValue error = scriptEngine->evaluate(QLatin1String("Error"));
     if (error.isValid())
diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanager.h b/src/plugins/coreplugin/scriptmanager/scriptmanager.h
index 8b3fcb2b09d..59397ce569c 100644
--- a/src/plugins/coreplugin/scriptmanager/scriptmanager.h
+++ b/src/plugins/coreplugin/scriptmanager/scriptmanager.h
@@ -34,7 +34,11 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QString>
-#include <QtScript/QScriptEngine>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QScriptEngine;
+QT_END_NAMESPACE
 
 namespace Core {
 
@@ -47,6 +51,8 @@ class CORE_EXPORT ScriptManager : public QObject
 {
     Q_OBJECT
 public:
+    typedef QSharedPointer<QScriptEngine> QScriptEnginePtr;
+
     // A stack frame as returned by a failed invocation (exception)
     // fileName may be empty. lineNumber can be 0 for the top frame (goof-up?).
     struct StackFrame {
@@ -62,6 +68,8 @@ public:
     // Run a script
     virtual bool runScript(const QString &script, QString *errorMessage, Stack *errorStack) = 0;
     virtual bool runScript(const QString &script, QString *errorMessage) = 0;
+
+    virtual QScriptEnginePtr scriptEngine() = 0;
 };
 
 } // namespace Core
diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanager_p.h b/src/plugins/coreplugin/scriptmanager/scriptmanager_p.h
index 4a34fd61d1a..f1e652c6bec 100644
--- a/src/plugins/coreplugin/scriptmanager/scriptmanager_p.h
+++ b/src/plugins/coreplugin/scriptmanager/scriptmanager_p.h
@@ -47,13 +47,14 @@ public:
 
     bool runScript(const QString &script, QString *errorMessage, Stack *stack);
     bool runScript(const QString &script, QString *errorMessage);
+    virtual QScriptEnginePtr scriptEngine() { return ensureEngineInitialized(); }
 
-    static QString engineError(QScriptEngine *scriptEngine);
+    static QString engineError(const QScriptEnginePtr &scriptEngine);
 
 private:
-    void ensureEngineInitialized();
+    QScriptEnginePtr ensureEngineInitialized();
 
-    QScriptEngine *m_engine;
+    QScriptEnginePtr m_engine;
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp
index 84fe6cacbbd..f43042fb53a 100644
--- a/src/plugins/debugger/script/scriptengine.cpp
+++ b/src/plugins/debugger/script/scriptengine.cpp
@@ -45,6 +45,8 @@
 
 #include <texteditor/itexteditor.h>
 #include <coreplugin/ifile.h>
+#include <coreplugin/scriptmanager/scriptmanager.h>
+#include <coreplugin/icore.h>
 
 #include <QtCore/QDateTime>
 #include <QtCore/QDebug>
@@ -64,7 +66,7 @@
 #include <QtScript/QScriptValue>
 #include <QtScript/QScriptValueIterator>
 
-//#define DEBUG_SCRIPT 1
+// #define DEBUG_SCRIPT 1
 #if DEBUG_SCRIPT
 #   define SDEBUG(s) qDebug() << s
 #else
@@ -124,7 +126,10 @@ void ScriptAgent::exceptionCatch(qint64 scriptId, const QScriptValue & exception
 {
     Q_UNUSED(scriptId)
     Q_UNUSED(exception)
-    SDEBUG("ScriptAgent::exceptionCatch: " << scriptId << &exception);
+    const QString msg = QString::fromLatin1("An exception was caught on %1: '%2'").
+                        arg(scriptId).arg(exception.toString());
+    SDEBUG(msg);
+    q->showDebuggerOutput(LogMisc, msg);
 }
 
 void ScriptAgent::exceptionThrow(qint64 scriptId, const QScriptValue &exception,
@@ -133,21 +138,26 @@ void ScriptAgent::exceptionThrow(qint64 scriptId, const QScriptValue &exception,
     Q_UNUSED(scriptId)
     Q_UNUSED(exception)
     Q_UNUSED(hasHandler)
-    SDEBUG("ScriptAgent::exceptionThrow: " << scriptId << &exception
-        << hasHandler);
+    const QString msg = QString::fromLatin1("An exception occurred on %1: '%2'").
+                        arg(scriptId).arg(exception.toString());
+    SDEBUG(msg);
+    q->showDebuggerOutput(LogMisc, msg);
 }
 
 void ScriptAgent::functionEntry(qint64 scriptId)
 {
     Q_UNUSED(scriptId)
-    q->maybeBreakNow(true);
+    q->showDebuggerOutput(LogMisc, QString::fromLatin1("Function entry occurred on %1").arg(scriptId));
+    q->checkForBreakCondition(true);
 }
 
 void ScriptAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue)
 {
     Q_UNUSED(scriptId)
     Q_UNUSED(returnValue)
-    SDEBUG("ScriptAgent::functionExit: " << scriptId << &returnValue);
+    const QString msg = QString::fromLatin1("Function exit occurred on %1: '%2'").arg(scriptId).arg(returnValue.toString());
+    SDEBUG(msg);
+    q->showDebuggerOutput(LogMisc, msg);
 }
 
 void ScriptAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumber)
@@ -156,7 +166,7 @@ void ScriptAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumb
     Q_UNUSED(scriptId)
     Q_UNUSED(lineNumber)
     Q_UNUSED(columnNumber)
-    q->maybeBreakNow(false);
+    q->checkForBreakCondition(false);
 }
 
 void ScriptAgent::scriptLoad(qint64 scriptId, const QString &program,
@@ -166,8 +176,7 @@ void ScriptAgent::scriptLoad(qint64 scriptId, const QString &program,
     Q_UNUSED(program)
     Q_UNUSED(fileName)
     Q_UNUSED(baseLineNumber)
-    SDEBUG("ScriptAgent::scriptLoad: " << program << fileName
-      << baseLineNumber);
+    q->showDebuggerOutput(LogMisc, QString::fromLatin1("Loaded: %1 id: %2").arg(fileName).arg(scriptId));
 }
 
 void ScriptAgent::scriptUnload(qint64 scriptId)
@@ -186,9 +195,6 @@ void ScriptAgent::scriptUnload(qint64 scriptId)
 ScriptEngine::ScriptEngine(DebuggerManager *manager)
     : IDebuggerEngine(manager)
 {
-    // created in startDebugger()
-    m_scriptEngine = 0;
-    m_scriptAgent = 0;
 }
 
 ScriptEngine::~ScriptEngine()
@@ -208,30 +214,45 @@ void ScriptEngine::shutdown()
 
 void ScriptEngine::exitDebugger()
 {
+    if (state() == DebuggerNotReady)
+        return;
     SDEBUG("ScriptEngine::exitDebugger()");
     m_stopped = false;
     m_stopOnNextLine = false;
-    m_scriptEngine->abortEvaluation();
-    manager()->notifyInferiorExited();
+    if (m_scriptEngine->isEvaluating())
+        m_scriptEngine->abortEvaluation();
+    setState(InferiorShuttingDown);
+    setState(InferiorShutDown);
+
+    setState(EngineShuttingDown);
+    m_scriptEngine->setAgent(0);
+    setState(DebuggerNotReady);
 }
 
 void ScriptEngine::startDebugger(const DebuggerStartParametersPtr &sp)
 {
-    if (!m_scriptEngine)
-        m_scriptEngine = new QScriptEngine(this);
+    setState(AdapterStarting);
+    if (m_scriptEngine.isNull())
+        m_scriptEngine = Core::ICore::instance()->scriptManager()->scriptEngine();
     if (!m_scriptAgent)
-        m_scriptAgent = new ScriptAgent(this, m_scriptEngine);
-    m_scriptEngine->setAgent(m_scriptAgent);
+        m_scriptAgent.reset(new ScriptAgent(this, m_scriptEngine.data()));
+    m_scriptEngine->setAgent(m_scriptAgent.data());
+    /* Keep the gui alive (have the engine call processEvents() while the script
+     * is run in the foreground). */
     m_scriptEngine->setProcessEventsInterval(1 /*ms*/);
 
     m_stopped = false;
     m_stopOnNextLine = false;
     m_scriptEngine->abortEvaluation();
 
-    QFileInfo fi(sp->executable);
-    m_scriptFileName = fi.absoluteFilePath();
+    setState(AdapterStarted);
+    setState(InferiorStarting);
+
+    m_scriptFileName = QFileInfo (sp->executable).absoluteFilePath();
     QFile scriptFile(m_scriptFileName);
-    if (!scriptFile.open(QIODevice::ReadOnly)) {
+    if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
+        manager()->showDebuggerOutput(LogError, QString::fromLatin1("Cannot open %1: %2").
+                                      arg(m_scriptFileName, scriptFile.errorString()));
         emit startFailed();
         return;
     }
@@ -241,6 +262,7 @@ void ScriptEngine::startDebugger(const DebuggerStartParametersPtr &sp)
     attemptBreakpointSynchronization();
     setState(InferiorRunningRequested);
     showStatusMessage(tr("Running requested..."), 5000);
+    manager()->showDebuggerOutput(LogMisc, QLatin1String("Running: ") + m_scriptFileName);
     QTimer::singleShot(0, this, SLOT(runInferior()));
     emit startSuccessful();
 }
@@ -252,34 +274,28 @@ void ScriptEngine::continueInferior()
     m_stopOnNextLine = false;
 }
 
-void ScriptEngine::runInferior()
+static const char *qtExtensionsC[] = {
+    "qt.core", "qt.gui", "qt.xml", "qt.svg", "qt.network",
+    "qt.sql", "qt.opengl", "qt.webkit", "qt.xmlpatterns", "qt.uitools"
+};
+
+bool ScriptEngine::importExtensions()
 {
-    //QDir dir(QApplication::applicationDirPath());
-    //if (dir.dirName() == QLatin1String("debug") || dir.dirName() == QLatin1String("release"))
-    //    dir.cdUp();
-    //dir.cdUp();
-    //dir.cdUp();
+    SDEBUG("ScriptEngine::importExtensions()");
+    QStringList extensions;
+    const int extCount = sizeof(qtExtensionsC)/sizeof(const char *);
+    for (int  e = 0; e < extCount; e++)
+        extensions.append(QLatin1String(qtExtensionsC[e]));
+    if (m_scriptEngine->importedExtensions().contains(extensions.front()))
+        return true;
     QDir dir("/home/apoenitz/dev/qtscriptgenerator");
     if (!dir.cd("plugins")) {
         fprintf(stderr, "plugins folder does not exist -- did you build the bindings?\n");
-        return;
+        return false;
     }
     QStringList paths = qApp->libraryPaths();
     paths <<  dir.absolutePath();
     qApp->setLibraryPaths(paths);
-
-    SDEBUG("ScriptEngine::runInferior()");
-    QStringList extensions;
-    extensions << "qt.core"
-               << "qt.gui"
-               << "qt.xml"
-               << "qt.svg"
-               << "qt.network"
-               << "qt.sql"
-               << "qt.opengl"
-               << "qt.webkit"
-               << "qt.xmlpatterns"
-               << "qt.uitools";
     QStringList failExtensions;
     foreach (const QString &ext, extensions) {
         QScriptValue ret = m_scriptEngine->importExtension(ext);
@@ -302,8 +318,26 @@ void ScriptEngine::runInferior()
                      qPrintable(failExtensions.join(", ")), qPrintable(dir.absolutePath()));
         }
     }
+    return failExtensions.isEmpty();
+}
 
-    QScriptValue result = m_scriptEngine->evaluate(m_scriptContents, m_scriptFileName);
+void ScriptEngine::runInferior()
+{
+    SDEBUG("ScriptEngine::runInferior()");
+    importExtensions();
+    setState(InferiorRunning);
+    const QScriptValue result = m_scriptEngine->evaluate(m_scriptContents, m_scriptFileName);
+    setState(InferiorStopping);
+    setState(InferiorStopped);
+    if (m_scriptEngine->hasUncaughtException()) {
+        QString msg = QString::fromLatin1("An exception occurred during execution at line: %1\n%2\n").
+                      arg(m_scriptEngine->uncaughtExceptionLineNumber()).arg(m_scriptEngine->uncaughtException().toString());
+        msg += m_scriptEngine->uncaughtExceptionBacktrace().join(QString(QLatin1Char('\n')));
+        showDebuggerOutput(LogMisc, msg);
+    } else {
+        showDebuggerOutput(LogMisc, QString::fromLatin1("Evaluation returns '%1'").arg(result.toString()));
+    }
+    exitDebugger();
 }
 
 void ScriptEngine::interruptInferior()
@@ -427,6 +461,7 @@ QList<Symbol> ScriptEngine::moduleSymbols(const QString & /*moduleName*/)
 //
 //////////////////////////////////////////////////////////////////////
 
+
 static WatchData m_toolTip;
 static QPoint m_toolTipPos;
 static QHash<QString, WatchData> m_toolTipCache;
@@ -521,44 +556,55 @@ void ScriptEngine::assignValueInDebugger(const QString &expression,
     updateLocals();
 }
 
-void ScriptEngine::maybeBreakNow(bool byFunction)
+static BreakpointData *findBreakPointByFunction(BreakHandler *handler,
+                                                const QString &functionName)
 {
-    QScriptContext *context = m_scriptEngine->currentContext();
-    QScriptContextInfo info(context);
+    const int count = handler->size();
+    for (int b = 0; b < count; b++) {
+        BreakpointData *data = handler->at(b);
+        if (data->funcName == functionName)
+            return data;
+    }
+    return 0;
+}
 
-    //
-    // Update breakpoints
-    //
-    QString functionName = info.functionName();
-    QString fileName = info.fileName();
-    int lineNumber = info.lineNumber();
-    if (byFunction)
-        lineNumber = info.functionStartLineNumber();
+static BreakpointData *findBreakPointByFileName(BreakHandler *handler,
+                                              int lineNumber,
+                                              const QString &fileName)
+{
+    const int count = handler->size();
+    for (int b = 0; b < count; b++) {
+        BreakpointData *data = handler->at(b);
+        if (lineNumber == data->lineNumber.toInt() && fileName == data->fileName)
+            return data;
+    }
+    return 0;
+}
 
-    BreakHandler *handler = manager()->breakHandler();
+bool ScriptEngine::checkForBreakCondition(bool byFunction)
+{
+    const QScriptContext *context = m_scriptEngine->currentContext();
+    const QScriptContextInfo info(context);
 
+    // Update breakpoints
+    const QString functionName = info.functionName();
+    const QString fileName = info.fileName();
+    const int lineNumber = byFunction? info.functionStartLineNumber() : info.lineNumber();
+    SDEBUG("checkForBreakCondition" << byFunction << functionName << lineNumber << fileName);
     if (m_stopOnNextLine) {
+        // Interrupt inferior
         m_stopOnNextLine = false;
     } else {
-        int index = 0;
-        for (; index != handler->size(); ++index) {
-            BreakpointData *data = handler->at(index);
-            if (byFunction) {
-                if (!functionName.isEmpty() && data->funcName == functionName)
-                    break;
-            } else {
-                if (info.lineNumber() == data->lineNumber.toInt()
-                        && fileName == data->fileName)
-                    break;
-            }
-        }
-
-        if (index == handler->size())
-            return;
+        if (byFunction && functionName.isEmpty())
+            return false;
+        BreakpointData *data = byFunction ?
+                               findBreakPointByFunction(manager()->breakHandler(), functionName) :
+                               findBreakPointByFileName(manager()->breakHandler(), lineNumber, fileName);
+        if (!data)
+            return false;
 
         // we just run into a breakpoint
         //SDEBUG("RESOLVING BREAKPOINT AT " << fileName << lineNumber);
-        BreakpointData *data = handler->at(index);
         data->bpLineNumber = QByteArray::number(lineNumber);
         data->bpFileName = fileName;
         data->bpFuncName = functionName;
@@ -567,15 +613,17 @@ void ScriptEngine::maybeBreakNow(bool byFunction)
         data->pending = false;
         data->updateMarker();
     }
-
+    setState(InferiorStopping);
     setState(InferiorStopped);
-    showStatusMessage(tr("Stopped."), 5000);
+    SDEBUG("Stopped at " << lineNumber << fileName);
+    showStatusMessage(tr("Stopped at %1:%2.").arg(fileName).arg(lineNumber), 5000);
 
     StackFrame frame;
     frame.file = fileName;      
     frame.line = lineNumber;
     manager()->gotoLocation(frame, true);
     updateLocals();
+    return true;
 }
 
 void ScriptEngine::updateLocals()
@@ -590,7 +638,7 @@ void ScriptEngine::updateLocals()
     QList<StackFrame> stackFrames;
     int i = 0;
     for (QScriptContext *c = context; c; c = c->parentContext(), ++i) {
-        QScriptContextInfo info(c);
+        const QScriptContextInfo info(c);
         StackFrame frame;
         frame.level = i;
         frame.file = info.fileName();
@@ -598,9 +646,8 @@ void ScriptEngine::updateLocals()
         frame.from = QString::number(info.functionStartLineNumber());
         frame.to = QString::number(info.functionEndLineNumber());
         frame.line = info.lineNumber();
-    
         if (frame.function.isEmpty())
-            frame.function = "<global scope>";
+            frame.function = QLatin1String("<global scope>");
         //frame.address = ...;
         stackFrames.append(frame);
     }
@@ -736,6 +783,11 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
     QTC_ASSERT(false, return);
 }
 
+void ScriptEngine::showDebuggerOutput(int channel, const QString &m)
+{
+    manager()->showDebuggerOutput(channel, m);
+}
+
 IDebuggerEngine *createScriptEngine(DebuggerManager *manager)
 {
     return new ScriptEngine(manager);
diff --git a/src/plugins/debugger/script/scriptengine.h b/src/plugins/debugger/script/scriptengine.h
index 9eea2cf3579..8af60d54a72 100644
--- a/src/plugins/debugger/script/scriptengine.h
+++ b/src/plugins/debugger/script/scriptengine.h
@@ -38,6 +38,8 @@
 #include <QtCore/QPoint>
 #include <QtCore/QSet>
 #include <QtCore/QVariant>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QScopedPointer>
 
 QT_BEGIN_NAMESPACE
 class QAction;
@@ -103,18 +105,22 @@ private:
     void reloadFullStack() {}
 
     bool supportsThreads() const { return true; }
-    void maybeBreakNow(bool byFunction);
+    bool checkForBreakCondition(bool byFunction);
     void updateWatchData(const WatchData &data);
     void updateLocals();
     void updateSubItem(const WatchData &data);
 
+    Q_SLOT void showDebuggerOutput(int channel, const QString &m);
+
 private:
     friend class ScriptAgent;
 
-    QScriptEngine *m_scriptEngine;
+    bool importExtensions();
+
+    QSharedPointer<QScriptEngine> m_scriptEngine;
     QString m_scriptContents;
     QString m_scriptFileName;
-    ScriptAgent *m_scriptAgent;
+    QScopedPointer<ScriptAgent> m_scriptAgent;
 
     bool m_stopped;
     bool m_stopOnNextLine;
-- 
GitLab