summaryrefslogtreecommitdiffstats
path: root/examples/script/qsdbg
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /examples/script/qsdbg
Long live Qt 4.5!
Diffstat (limited to 'examples/script/qsdbg')
-rw-r--r--examples/script/qsdbg/example.qs17
-rw-r--r--examples/script/qsdbg/main.cpp74
-rw-r--r--examples/script/qsdbg/qsdbg.pri9
-rw-r--r--examples/script/qsdbg/qsdbg.pro19
-rw-r--r--examples/script/qsdbg/scriptbreakpointmanager.cpp159
-rw-r--r--examples/script/qsdbg/scriptbreakpointmanager.h122
-rw-r--r--examples/script/qsdbg/scriptdebugger.cpp737
-rw-r--r--examples/script/qsdbg/scriptdebugger.h85
8 files changed, 1222 insertions, 0 deletions
diff --git a/examples/script/qsdbg/example.qs b/examples/script/qsdbg/example.qs
new file mode 100644
index 0000000000..47c1363d49
--- /dev/null
+++ b/examples/script/qsdbg/example.qs
@@ -0,0 +1,17 @@
+function bar() {
+ var x = 1;
+ var y = 2;
+ return x + y;
+}
+
+function foo(a, b, c) {
+ var i = a + bar();
+ var j = b - bar();
+ var k = c * bar();
+ return Math.cos(i) + Math.sin(j) - Math.atan(k);
+}
+
+var first = foo(1, 2, 3);
+var second = foo(4, 5, 6);
+print("first was:", first, ", and second was:", second);
+
diff --git a/examples/script/qsdbg/main.cpp b/examples/script/qsdbg/main.cpp
new file mode 100644
index 0000000000..eb377cf2a5
--- /dev/null
+++ b/examples/script/qsdbg/main.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtScript>
+
+#include "scriptdebugger.h"
+
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ fprintf(stderr, "*** you must specify a script file to evaluate (try example.qs)\n");
+ return(-1);
+ }
+
+ QString fileName = QString::fromLatin1(argv[1]);
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ fprintf(stderr, "*** failed to open `%s' for reading\n", argv[1]);
+ return(-1);
+ }
+
+ QScriptEngine engine;
+ QString code = QTextStream(&file).readAll();
+ file.close();
+
+ fprintf(stdout, "\n*** Welcome to qsdbg. Debugger commands start with a . (period)\n");
+ fprintf(stdout, "*** Any other input will be evaluated by the script interpreter.\n");
+ fprintf(stdout, "*** Type .help for help.\n\n");
+
+ ScriptDebugger *dbg = new ScriptDebugger(&engine);
+ dbg->breakAtNextStatement();
+
+ engine.evaluate(code, fileName);
+
+ return 0;
+}
diff --git a/examples/script/qsdbg/qsdbg.pri b/examples/script/qsdbg/qsdbg.pri
new file mode 100644
index 0000000000..618e623e6a
--- /dev/null
+++ b/examples/script/qsdbg/qsdbg.pri
@@ -0,0 +1,9 @@
+SOURCES += \
+ $$PWD/scriptdebugger.cpp \
+ $$PWD/scriptbreakpointmanager.cpp
+
+HEADERS += \
+ $$PWD/scriptdebugger.h \
+ $$PWD/scriptbreakpointmanager.h
+
+INCLUDEPATH += $$PWD
diff --git a/examples/script/qsdbg/qsdbg.pro b/examples/script/qsdbg/qsdbg.pro
new file mode 100644
index 0000000000..c1991230e3
--- /dev/null
+++ b/examples/script/qsdbg/qsdbg.pro
@@ -0,0 +1,19 @@
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += .
+QT += script
+win32: CONFIG += console
+mac: CONFIG -= app_bundle
+
+SOURCES += main.cpp
+
+include(qsdbg.pri)
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/script/qsdbg
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS qsdbg.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/script/qsdbg
+INSTALLS += target sources
+
+
diff --git a/examples/script/qsdbg/scriptbreakpointmanager.cpp b/examples/script/qsdbg/scriptbreakpointmanager.cpp
new file mode 100644
index 0000000000..7668d7bcb8
--- /dev/null
+++ b/examples/script/qsdbg/scriptbreakpointmanager.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "scriptbreakpointmanager.h"
+
+ScriptBreakpointManager::ScriptBreakpointManager()
+{
+}
+
+ScriptBreakpointManager::~ScriptBreakpointManager()
+{
+}
+
+bool ScriptBreakpointManager::hasBreakpoints() const
+{
+ return !breakpoints.isEmpty();
+}
+
+int ScriptBreakpointManager::setBreakpoint(const QString &fileName, int lineNumber)
+{
+ breakpoints.append(ScriptBreakpointInfo(fileName, lineNumber));
+ return breakpoints.size() - 1;
+}
+
+int ScriptBreakpointManager::setBreakpoint(const QString &functionName, const QString &fileName)
+{
+ breakpoints.append(ScriptBreakpointInfo(functionName, fileName));
+ return breakpoints.size() - 1;
+}
+
+int ScriptBreakpointManager::setBreakpoint(const QScriptValue &function)
+{
+ breakpoints.append(ScriptBreakpointInfo(function));
+ return breakpoints.size() - 1;
+}
+
+void ScriptBreakpointManager::removeBreakpoint(int id)
+{
+ if (id >= 0 && id < breakpoints.size())
+ breakpoints[id] = ScriptBreakpointInfo();
+}
+
+int ScriptBreakpointManager::findBreakpoint(const QString &fileName, int lineNumber) const
+{
+ for (int i = 0; i < breakpoints.size(); ++i) {
+ const ScriptBreakpointInfo &brk = breakpoints.at(i);
+ if (brk.type != ScriptBreakpointInfo::File)
+ continue;
+ if (brk.fileName == fileName && brk.lineNumber == lineNumber)
+ return i;
+ }
+ return -1;
+}
+
+int ScriptBreakpointManager::findBreakpoint(const QString &functionName, const QString &fileName) const
+{
+ for (int i = 0; i < breakpoints.size(); ++i) {
+ const ScriptBreakpointInfo &brk = breakpoints.at(i);
+ if (brk.type != ScriptBreakpointInfo::FunctionName)
+ continue;
+ if (brk.functionName == functionName && brk.fileName == fileName)
+ return i;
+ }
+ return -1;
+}
+
+int ScriptBreakpointManager::findBreakpoint(const QScriptValue &function) const
+{
+ for (int i = 0; i < breakpoints.size(); ++i) {
+ const ScriptBreakpointInfo &brk = breakpoints.at(i);
+ if (brk.type != ScriptBreakpointInfo::Function)
+ continue;
+ if (brk.function.strictlyEquals(function))
+ return i;
+ }
+ return -1;
+}
+
+bool ScriptBreakpointManager::isBreakpointEnabled(int id) const
+{
+ return breakpoints.value(id).enabled;
+}
+
+void ScriptBreakpointManager::setBreakpointEnabled(int id, bool enabled)
+{
+ if (id >= 0 && id < breakpoints.size())
+ breakpoints[id].enabled = enabled;
+}
+
+QString ScriptBreakpointManager::breakpointCondition(int id) const
+{
+ return breakpoints.value(id).condition;
+}
+
+void ScriptBreakpointManager::setBreakpointCondition(int id, const QString &expression)
+{
+ if (id >= 0 && id < breakpoints.size())
+ breakpoints[id].condition = expression;
+}
+
+int ScriptBreakpointManager::breakpointIgnoreCount(int id) const
+{
+ return breakpoints.value(id).ignoreCount;
+}
+
+void ScriptBreakpointManager::setBreakpointIgnoreCount(int id, int ignoreCount)
+{
+ if (id >= 0 && id < breakpoints.size())
+ breakpoints[id].ignoreCount = ignoreCount;
+}
+
+bool ScriptBreakpointManager::isBreakpointSingleShot(int id) const
+{
+ return breakpoints.value(id).singleShot;
+}
+
+void ScriptBreakpointManager::setBreakpointSingleShot(int id, bool singleShot)
+{
+ if (id >= 0 && id < breakpoints.size())
+ breakpoints[id].singleShot = singleShot;
+}
diff --git a/examples/script/qsdbg/scriptbreakpointmanager.h b/examples/script/qsdbg/scriptbreakpointmanager.h
new file mode 100644
index 0000000000..cdb176c7b5
--- /dev/null
+++ b/examples/script/qsdbg/scriptbreakpointmanager.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SCRIPTBREAKPOINTMANAGER_H
+#define SCRIPTBREAKPOINTMANAGER_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+#include <QtScript/qscriptvalue.h>
+
+class ScriptBreakpointInfo
+{
+public:
+ enum Type {
+ File,
+ FunctionName,
+ Function,
+ Invalid
+ };
+
+ Type type;
+ QString functionName;
+ QString fileName;
+ int lineNumber;
+ QScriptValue function;
+ bool enabled;
+ QString condition;
+ int ignoreCount;
+ bool singleShot;
+
+ ScriptBreakpointInfo(const QString &fileName, int lineNumber)
+ : type(File), fileName(fileName), lineNumber(lineNumber),
+ enabled(true), ignoreCount(0), singleShot(false)
+ { }
+ ScriptBreakpointInfo(const QString &functionName, const QString &fileName = QString())
+ : type(FunctionName), functionName(functionName), fileName(fileName),
+ enabled(true), ignoreCount(0), singleShot(false)
+ { }
+ ScriptBreakpointInfo(const QScriptValue &function)
+ : type(Function), function(function),
+ enabled(true), ignoreCount(0), singleShot(false)
+ { }
+ ScriptBreakpointInfo()
+ : type(Invalid)
+ { }
+};
+
+class ScriptBreakpointManager
+{
+public:
+ ScriptBreakpointManager();
+ ~ScriptBreakpointManager();
+
+ bool hasBreakpoints() const;
+
+ int setBreakpoint(const QString &fileName, int lineNumber);
+ int setBreakpoint(const QString &functionName, const QString &fileName = QString());
+ int setBreakpoint(const QScriptValue &function);
+
+ void removeBreakpoint(int id);
+
+ int findBreakpoint(const QString &fileName, int lineNumber) const;
+ int findBreakpoint(const QString &functionName, const QString &fileName = QString()) const;
+ int findBreakpoint(const QScriptValue &function) const;
+
+ bool isBreakpointEnabled(int id) const;
+ void setBreakpointEnabled(int id, bool enabled);
+
+ QString breakpointCondition(int id) const;
+ void setBreakpointCondition(int id, const QString &expression);
+
+ int breakpointIgnoreCount(int id) const;
+ void setBreakpointIgnoreCount(int id, int ignoreCount);
+
+ bool isBreakpointSingleShot(int id) const;
+ void setBreakpointSingleShot(int id, bool singleShot);
+
+private:
+ QList<ScriptBreakpointInfo> breakpoints;
+
+ Q_DISABLE_COPY(ScriptBreakpointManager)
+};
+
+#endif // SCRIPTBREAKPOINTMANAGER_H
diff --git a/examples/script/qsdbg/scriptdebugger.cpp b/examples/script/qsdbg/scriptdebugger.cpp
new file mode 100644
index 0000000000..e3639b93bc
--- /dev/null
+++ b/examples/script/qsdbg/scriptdebugger.cpp
@@ -0,0 +1,737 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "scriptdebugger.h"
+#include "scriptbreakpointmanager.h"
+
+#include <QtScript/QScriptEngine>
+#include <QtScript/QScriptEngineAgent>
+#include <QtScript/QScriptContextInfo>
+#include <QtScript/QScriptValueIterator>
+#include <QtCore/QTextStream>
+#include <QtCore/QStack>
+
+static QString safeValueToString(const QScriptValue &value)
+{
+ if (value.isObject())
+ return QLatin1String("[object Object]");
+ else
+ return value.toString();
+}
+
+class ScriptInfo;
+class ScriptBreakpointManager;
+
+class ScriptDebuggerPrivate
+ : public QScriptEngineAgent
+{
+ Q_DECLARE_PUBLIC(ScriptDebugger)
+public:
+ enum Mode {
+ Run,
+ StepInto,
+ StepOver
+ };
+
+ ScriptDebuggerPrivate(QScriptEngine *engine);
+ ~ScriptDebuggerPrivate();
+
+ // QScriptEngineAgent interface
+ void scriptLoad(qint64 id, const QString &program,
+ const QString &fileName, int lineNumber);
+ void scriptUnload(qint64 id);
+
+ void positionChange(qint64 scriptId,
+ int lineNumber, int columnNumber);
+
+ void functionEntry(qint64 scriptId);
+ void functionExit(qint64 scriptId,
+ const QScriptValue &returnValue);
+
+ void exceptionThrow(qint64 scriptId,
+ const QScriptValue &exception, bool hasHandler);
+
+
+ void interactive();
+ bool executeCommand(const QString &command, const QStringList &args);
+
+ void setMode(Mode mode);
+ Mode mode() const;
+
+ int frameCount() const;
+ void setCurrentFrameIndex(int index);
+ int currentFrameIndex() const;
+
+ QScriptContext *frameContext(int index) const;
+ QScriptContext *currentFrameContext() const;
+
+ ScriptInfo *scriptInfo(QScriptContext *context) const;
+
+ int listLineNumber() const;
+ void setListLineNumber(int lineNumber);
+
+ QString readLine();
+ void output(const QString &text);
+ void message(const QString &text);
+ void errorMessage(const QString &text);
+
+ // attributes
+ QTextStream *m_defaultInputStream;
+ QTextStream *m_defaultOutputStream;
+ QTextStream *m_defaultErrorStream;
+ QTextStream *m_inputStream;
+ QTextStream *m_outputStream;
+ QTextStream *m_errorStream;
+
+ ScriptBreakpointManager *m_bpManager;
+ Mode m_mode;
+ QMap<qint64, ScriptInfo*> m_scripts;
+ QMap<QScriptContext*, QStack<qint64> > m_contextProgramIds;
+
+ QString m_lastInteractiveCommand;
+ QString m_commandPrefix;
+ int m_stepDepth;
+ int m_currentFrameIndex;
+ int m_listLineNumber;
+
+ ScriptDebugger *q_ptr;
+};
+
+class ScriptInfo
+{
+public:
+ ScriptInfo(const QString &code, const QString &fileName, int lineNumber)
+ : m_code(code), m_fileName(fileName), m_lineNumber(lineNumber)
+ { }
+
+ inline QString code() const
+ { return m_code; }
+ inline QString fileName() const
+ { return m_fileName; }
+ inline int lineNumber() const
+ { return m_lineNumber; }
+
+ QString lineText(int lineNumber);
+ QMap<int, int> m_lineOffsets;
+
+private:
+ int lineOffset(int lineNumber);
+
+ QString m_code;
+ QString m_fileName;
+ int m_lineNumber;
+};
+
+int ScriptInfo::lineOffset(int lineNumber)
+{
+ QMap<int, int>::const_iterator it = m_lineOffsets.constFind(lineNumber);
+ if (it != m_lineOffsets.constEnd())
+ return it.value();
+
+ int offset;
+ it = m_lineOffsets.constFind(lineNumber - 1);
+ if (it != m_lineOffsets.constEnd()) {
+ offset = it.value();
+ offset = m_code.indexOf(QLatin1Char('\n'), offset);
+ if (offset != -1)
+ ++offset;
+ m_lineOffsets.insert(lineNumber, offset);
+ } else {
+ int index;
+ it = m_lineOffsets.lowerBound(lineNumber);
+ --it;
+ if (it != m_lineOffsets.constBegin()) {
+ index = it.key();
+ offset = it.value();
+ } else {
+ index = m_lineNumber;
+ offset = 0;
+ }
+ int j = index;
+ for ( ; j < lineNumber; ++j) {
+ m_lineOffsets.insert(j, offset);
+ offset = m_code.indexOf(QLatin1Char('\n'), offset);
+ if (offset == -1)
+ break;
+ ++offset;
+ }
+ m_lineOffsets.insert(j, offset);
+ }
+ return offset;
+}
+
+QString ScriptInfo::lineText(int lineNumber)
+{
+ int startOffset = lineOffset(lineNumber);
+ if (startOffset == -1)
+ return QString();
+ int endOffset = lineOffset(lineNumber + 1);
+ if (endOffset == -1)
+ return m_code.mid(startOffset);
+ else
+ return m_code.mid(startOffset, endOffset - startOffset - 1);
+}
+
+
+
+ScriptDebuggerPrivate::ScriptDebuggerPrivate(QScriptEngine *engine)
+ : QScriptEngineAgent(engine), m_mode(Run)
+{
+ m_commandPrefix = QLatin1String(".");
+ m_bpManager = new ScriptBreakpointManager;
+ m_defaultInputStream = new QTextStream(stdin);
+ m_defaultOutputStream = new QTextStream(stdout);
+ m_defaultErrorStream = new QTextStream(stderr);
+ m_inputStream = m_defaultInputStream;
+ m_outputStream = m_defaultOutputStream;
+ m_errorStream = m_defaultErrorStream;
+}
+
+ScriptDebuggerPrivate::~ScriptDebuggerPrivate()
+{
+ delete m_defaultInputStream;
+ delete m_defaultOutputStream;
+ delete m_defaultErrorStream;
+ delete m_bpManager;
+ qDeleteAll(m_scripts);
+}
+
+QString ScriptDebuggerPrivate::readLine()
+{
+ return m_inputStream->readLine();
+}
+
+void ScriptDebuggerPrivate::output(const QString &text)
+{
+ *m_outputStream << text;
+}
+
+void ScriptDebuggerPrivate::message(const QString &text)
+{
+ *m_outputStream << text << endl;
+ m_outputStream->flush();
+}
+
+void ScriptDebuggerPrivate::errorMessage(const QString &text)
+{
+ *m_errorStream << text << endl;
+ m_errorStream->flush();
+}
+
+void ScriptDebuggerPrivate::setMode(Mode mode)
+{
+ m_mode = mode;
+}
+
+ScriptDebuggerPrivate::Mode ScriptDebuggerPrivate::mode() const
+{
+ return m_mode;
+}
+
+QScriptContext *ScriptDebuggerPrivate::frameContext(int index) const
+{
+ QScriptContext *ctx = engine()->currentContext();
+ for (int i = 0; i < index; ++i) {
+ ctx = ctx->parentContext();
+ if (!ctx)
+ break;
+ }
+ return ctx;
+}
+
+int ScriptDebuggerPrivate::currentFrameIndex() const
+{
+ return m_currentFrameIndex;
+}
+
+void ScriptDebuggerPrivate::setCurrentFrameIndex(int index)
+{
+ m_currentFrameIndex = index;
+ m_listLineNumber = -1;
+}
+
+int ScriptDebuggerPrivate::listLineNumber() const
+{
+ return m_listLineNumber;
+}
+
+void ScriptDebuggerPrivate::setListLineNumber(int lineNumber)
+{
+ m_listLineNumber = lineNumber;
+}
+
+QScriptContext *ScriptDebuggerPrivate::currentFrameContext() const
+{
+ return frameContext(currentFrameIndex());
+}
+
+int ScriptDebuggerPrivate::frameCount() const
+{
+ int count = 0;
+ QScriptContext *ctx = engine()->currentContext();
+ while (ctx) {
+ ++count;
+ ctx = ctx->parentContext();
+ }
+ return count;
+}
+
+ScriptInfo *ScriptDebuggerPrivate::scriptInfo(QScriptContext *context) const
+{
+ QStack<qint64> pids = m_contextProgramIds.value(context);
+ if (pids.isEmpty())
+ return 0;
+ return m_scripts.value(pids.top());
+}
+
+void ScriptDebuggerPrivate::interactive()
+{
+ setCurrentFrameIndex(0);
+
+ QString qsdbgPrompt = QString::fromLatin1("(qsdbg) ");
+ QString dotPrompt = QString::fromLatin1(".... ");
+ QString prompt = qsdbgPrompt;
+
+ QString code;
+
+ forever {
+
+ *m_outputStream << prompt;
+ m_outputStream->flush();
+
+ QString line = readLine();
+
+ if (code.isEmpty() && (line.isEmpty() || line.startsWith(m_commandPrefix))) {
+ if (line.isEmpty())
+ line = m_lastInteractiveCommand;
+ else
+ m_lastInteractiveCommand = line;
+
+ QStringList parts = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ if (!parts.isEmpty()) {
+ QString command = parts.takeFirst().mid(1);
+ if (executeCommand(command, parts))
+ break;
+ }
+
+ } else {
+ if (line.isEmpty())
+ continue;
+
+ code += line;
+ code += QLatin1Char('\n');
+
+ if (line.trimmed().isEmpty()) {
+ continue;
+
+ } else if (! engine()->canEvaluate(code)) {
+ prompt = dotPrompt;
+
+ } else {
+ setMode(Run);
+ QScriptValue result = engine()->evaluate(code, QLatin1String("typein"));
+
+ code.clear();
+ prompt = qsdbgPrompt;
+
+ if (! result.isUndefined()) {
+ errorMessage(result.toString());
+ engine()->clearExceptions();
+ }
+ }
+ }
+ }
+}
+
+bool ScriptDebuggerPrivate::executeCommand(const QString &command, const QStringList &args)
+{
+ if (command == QLatin1String("c")
+ || command == QLatin1String("continue")) {
+ setMode(Run);
+ return true;
+ } else if (command == QLatin1String("s")
+ || command == QLatin1String("step")) {
+ setMode(StepInto);
+ return true;
+ } else if (command == QLatin1String("n")
+ || command == QLatin1String("next")) {
+ setMode(StepOver);
+ m_stepDepth = 0;
+ return true;
+ } else if (command == QLatin1String("f")
+ || command == QLatin1String("frame")) {
+ bool ok = false;
+ int index = args.value(0).toInt(&ok);
+ if (ok) {
+ if (index < 0 || index >= frameCount()) {
+ errorMessage("No such frame.");
+ } else {
+ setCurrentFrameIndex(index);
+ QScriptContext *ctx = currentFrameContext();
+ message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
+ }
+ }
+ } else if (command == QLatin1String("bt")
+ || command == QLatin1String("backtrace")) {
+ QScriptContext *ctx = engine()->currentContext();
+ int index = -1;
+ while (ctx) {
+ ++index;
+ QString line = ctx->toString();
+ message(QString::fromLatin1("#%0 %1").arg(index).arg(line));
+ ctx = ctx->parentContext();
+ }
+ } else if (command == QLatin1String("up")) {
+ int index = currentFrameIndex() + 1;
+ if (index == frameCount()) {
+ errorMessage(QString::fromLatin1("Initial frame selected; you cannot go up."));
+ } else {
+ setCurrentFrameIndex(index);
+ QScriptContext *ctx = currentFrameContext();
+ message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
+ }
+ } else if (command == QLatin1String("down")) {
+ int index = currentFrameIndex() - 1;
+ if (index < 0) {
+ errorMessage(QString::fromLatin1("Bottom (innermost) frame selected; you cannot go down."));
+ } else {
+ setCurrentFrameIndex(index);
+ QScriptContext *ctx = currentFrameContext();
+ message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
+ }
+ } else if (command == QLatin1String("b")
+ || command == QLatin1String("break")) {
+ QString str = args.value(0);
+ int colonIndex = str.indexOf(QLatin1Char(':'));
+ if (colonIndex != -1) {
+ // filename:line form
+ QString fileName = str.left(colonIndex);
+ int lineNumber = str.mid(colonIndex+1).toInt();
+ int id = m_bpManager->setBreakpoint(fileName, lineNumber);
+ message(QString::fromLatin1("Breakpoint %0 at %1, line %2.").arg(id+1).arg(fileName).arg(lineNumber));
+ } else {
+ // function
+ QScriptValue fun = engine()->globalObject().property(str);
+ if (fun.isFunction()) {
+ int id = m_bpManager->setBreakpoint(fun);
+ message(QString::fromLatin1("Breakpoint %0 at %1().").arg(id+1).arg(str));
+ }
+ }
+ } else if (command == QLatin1String("d")
+ || command == QLatin1String("delete")) {
+ int id = args.value(0).toInt() - 1;
+ m_bpManager->removeBreakpoint(id);
+ } else if (command == QLatin1String("disable")) {
+ int id = args.value(0).toInt() - 1;
+ m_bpManager->setBreakpointEnabled(id, false);
+ } else if (command == QLatin1String("enable")) {
+ int id = args.value(0).toInt() - 1;
+ m_bpManager->setBreakpointEnabled(id, true);
+ } else if (command == QLatin1String("list")) {
+ QScriptContext *ctx = currentFrameContext();
+ ScriptInfo *progInfo = scriptInfo(ctx);
+ if (!progInfo) {
+ errorMessage("No source text available for this frame.");
+ } else {
+ QScriptContextInfo ctxInfo(ctx);
+ bool ok;
+ int line = args.value(0).toInt(&ok);
+ if (ok) {
+ line = qMax(1, line - 5);
+ } else {
+ line = listLineNumber();
+ if (line == -1)
+ line = qMax(progInfo->lineNumber(), ctxInfo.lineNumber() - 5);
+ }
+ for (int i = line; i < line + 10; ++i) {
+ message(QString::fromLatin1("%0\t%1").arg(i).arg(progInfo->lineText(i)));
+ }
+ setListLineNumber(line + 10);
+ }
+ } else if (command == QLatin1String("info")) {
+ if (args.size() < 1) {
+ } else {
+ QString what = args.value(0);
+ if (what == QLatin1String("locals")) {
+ QScriptValueIterator it(currentFrameContext()->activationObject());
+ while (it.hasNext()) {
+ it.next();
+ QString line;
+ line.append(it.name());
+ line.append(QLatin1String(" = "));
+ line.append(safeValueToString(it.value()));
+ message(line);
+ }
+ }
+ }
+ } else if (command == QLatin1String("help")) {
+ message("continue - continue execution\n"
+ "step - step into statement\n"
+ "next - step over statement\n"
+ "list - show where you are\n"
+ "\n"
+ "break - set breakpoint\n"
+ "delete - remove breakpoint\n"
+ "disable - disable breakpoint\n"
+ "enable - enable breakpoint\n"
+ "\n"
+ "backtrace - show backtrace\n"
+ "up - one frame up\n"
+ "down - one frame down\n"
+ "frame - set frame\n"
+ "\n"
+ "info locals - show local variables");
+ } else {
+ errorMessage(QString::fromLatin1("Undefined command \"%0\". Try \"help\".")
+ .arg(command));
+ }
+
+ return false;
+}
+
+
+// QScriptEngineAgent interface
+
+void ScriptDebuggerPrivate::scriptLoad(qint64 id, const QString &program,
+ const QString &fileName, int lineNumber)
+{
+ ScriptInfo *info = new ScriptInfo(program, fileName, lineNumber);
+ m_scripts.insert(id, info);
+}
+
+void ScriptDebuggerPrivate::scriptUnload(qint64 id)
+{
+ ScriptInfo *info = m_scripts.take(id);
+ delete info;
+}
+
+void ScriptDebuggerPrivate::functionEntry(qint64 scriptId)
+{
+ if (scriptId != -1) {
+ QScriptContext *ctx = engine()->currentContext();
+ QStack<qint64> ids = m_contextProgramIds.value(ctx);
+ ids.push(scriptId);
+ m_contextProgramIds.insert(ctx, ids);
+ }
+
+ if (mode() == StepOver)
+ ++m_stepDepth;
+}
+
+void ScriptDebuggerPrivate::functionExit(qint64 scriptId,
+ const QScriptValue &/*returnValue*/)
+{
+ if (scriptId != -1) {
+ QScriptContext *ctx = engine()->currentContext();
+ QStack<qint64> ids = m_contextProgramIds.value(ctx);
+ Q_ASSERT(!ids.isEmpty());
+ Q_ASSERT(ids.top() == scriptId);
+ ids.pop();
+ m_contextProgramIds.insert(ctx, ids);
+ }
+
+ if (mode() == StepOver)
+ --m_stepDepth;
+}
+
+void ScriptDebuggerPrivate::positionChange(qint64 scriptId,
+ int lineNumber, int /*columnNumber*/)
+{
+ ScriptInfo *info = 0;
+ bool enterInteractiveMode = false;
+
+ if (m_bpManager->hasBreakpoints()) {
+ // check if we hit a breakpoint
+ info = m_scripts.value(scriptId);
+ QScriptContext *ctx = engine()->currentContext();
+ QScriptContextInfo ctxInfo(ctx);
+ QScriptValue callee = ctx->callee();
+
+ // try fileName:lineNumber
+ int bpid = m_bpManager->findBreakpoint(info->fileName(), lineNumber);
+ if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
+ message(QString::fromLatin1("Breakpoint %0 at %1:%2")
+ .arg(bpid + 1).arg(info->fileName()).arg(lineNumber));
+ if (m_bpManager->isBreakpointSingleShot(bpid))
+ m_bpManager->removeBreakpoint(bpid);
+ }
+ if (bpid == -1) {
+ // try function
+ bpid = m_bpManager->findBreakpoint(callee);
+ if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
+ message(QString::fromLatin1("Breakpoint %0, %1()")
+ .arg(bpid + 1).arg(ctxInfo.functionName()));
+ if (m_bpManager->isBreakpointSingleShot(bpid))
+ m_bpManager->removeBreakpoint(bpid);
+ }
+ }
+ if ((bpid == -1) && !ctxInfo.functionName().isEmpty()) {
+ // try functionName:fileName
+ bpid = m_bpManager->findBreakpoint(ctxInfo.functionName(), ctxInfo.fileName());
+ if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
+ message(QString::fromLatin1("Breakpoint %0, %1():%2").arg(bpid + 1)
+ .arg(ctxInfo.functionName()).arg(ctxInfo.fileName()));
+ if (m_bpManager->isBreakpointSingleShot(bpid))
+ m_bpManager->removeBreakpoint(bpid);
+ }
+ }
+
+ enterInteractiveMode = (bpid != -1);
+ }
+
+ switch (mode()) {
+ case Run:
+ break;
+
+ case StepInto:
+ enterInteractiveMode = true;
+ break;
+
+ case StepOver:
+ enterInteractiveMode = enterInteractiveMode || (m_stepDepth <= 0);
+ break;
+ }
+
+ if (enterInteractiveMode) {
+ if (!info)
+ info = m_scripts.value(scriptId);
+ Q_ASSERT(info);
+ message(QString::fromLatin1("%0\t%1").arg(lineNumber).arg(info->lineText(lineNumber)));
+ interactive();
+ }
+}
+
+void ScriptDebuggerPrivate::exceptionThrow(qint64 /*scriptId*/,
+ const QScriptValue &exception,
+ bool hasHandler)
+{
+ if (!hasHandler) {
+ errorMessage(QString::fromLatin1("uncaught exception: %0").arg(exception.toString()));
+ QScriptContext *ctx = engine()->currentContext();
+ int lineNumber = QScriptContextInfo(ctx).lineNumber();
+ ScriptInfo *info = scriptInfo(ctx);
+ QString lineText = info ? info->lineText(lineNumber) : QString("(no source text available)");
+ message(QString::fromLatin1("%0\t%1").arg(lineNumber).arg(lineText));
+ interactive();
+ }
+}
+
+
+
+ScriptDebugger::ScriptDebugger(QScriptEngine *engine)
+ : d_ptr(new ScriptDebuggerPrivate(engine))
+{
+ d_ptr->q_ptr = this;
+ engine->setAgent(d_ptr);
+}
+
+ScriptDebugger::ScriptDebugger(QScriptEngine *engine, ScriptDebuggerPrivate &dd)
+ : d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+ engine->setAgent(d_ptr);
+}
+
+ScriptDebugger::~ScriptDebugger()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+
+void ScriptDebugger::breakAtNextStatement()
+{
+ Q_D(ScriptDebugger);
+ d->setMode(ScriptDebuggerPrivate::StepInto);
+}
+
+void ScriptDebugger::setBreakpoint(const QString &fileName, int lineNumber)
+{
+ Q_D(ScriptDebugger);
+ d->m_bpManager->setBreakpoint(fileName, lineNumber);
+}
+
+void ScriptDebugger::setBreakpoint(const QString &functionName, const QString &fileName)
+{
+ Q_D(ScriptDebugger);
+ d->m_bpManager->setBreakpoint(functionName, fileName);
+}
+
+void ScriptDebugger::setBreakpoint(const QScriptValue &function)
+{
+ Q_D(ScriptDebugger);
+ d->m_bpManager->setBreakpoint(function);
+}
+
+QTextStream *ScriptDebugger::inputStream() const
+{
+ Q_D(const ScriptDebugger);
+ return d->m_inputStream;
+}
+
+void ScriptDebugger::setInputStream(QTextStream *inputStream)
+{
+ Q_D(ScriptDebugger);
+ d->m_inputStream = inputStream;
+}
+
+QTextStream *ScriptDebugger::outputStream() const
+{
+ Q_D(const ScriptDebugger);
+ return d->m_outputStream;
+}
+
+void ScriptDebugger::setOutputStream(QTextStream *outputStream)
+{
+ Q_D(ScriptDebugger);
+ d->m_outputStream = outputStream;
+}
+
+QTextStream *ScriptDebugger::errorStream() const
+{
+ Q_D(const ScriptDebugger);
+ return d->m_errorStream;
+}
+
+void ScriptDebugger::setErrorStream(QTextStream *errorStream)
+{
+ Q_D(ScriptDebugger);
+ d->m_errorStream = errorStream;
+}
diff --git a/examples/script/qsdbg/scriptdebugger.h b/examples/script/qsdbg/scriptdebugger.h
new file mode 100644
index 0000000000..c33adf65c7
--- /dev/null
+++ b/examples/script/qsdbg/scriptdebugger.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SCRIPTDEBUGGER_H
+#define SCRIPTDEBUGGER_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+class QScriptEngine;
+class QScriptValue;
+class QTextStream;
+QT_END_NAMESPACE
+
+class ScriptDebuggerPrivate;
+class ScriptDebugger
+{
+public:
+ ScriptDebugger(QScriptEngine *engine);
+ virtual ~ScriptDebugger();
+
+ void breakAtNextStatement();
+
+ void setBreakpoint(const QString &fileName, int lineNumber);
+ void setBreakpoint(const QString &functionName, const QString &fileName = QString());
+ void setBreakpoint(const QScriptValue &function);
+
+ QTextStream *inputStream() const;
+ void setInputStream(QTextStream *inputStream);
+
+ QTextStream *outputStream() const;
+ void setOutputStream(QTextStream *outputStream);
+
+ QTextStream *errorStream() const;
+ void setErrorStream(QTextStream *errorStream);
+
+protected:
+ ScriptDebugger(QScriptEngine *engine, ScriptDebuggerPrivate &dd);
+ ScriptDebuggerPrivate *d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(ScriptDebugger)
+ Q_DISABLE_COPY(ScriptDebugger)
+};
+
+#endif // SCRIPTDEBUGGER_H