diff --git a/src/tools/qml/qmlviewer/jsdebuggeragent.cpp b/src/tools/qml/qmlviewer/jsdebuggeragent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ede07ebda5750894f969876af6dfea7d81aa7624 --- /dev/null +++ b/src/tools/qml/qmlviewer/jsdebuggeragent.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSCriptTools module 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsdebuggeragent.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qset.h> +#include <QtScript/qscriptengine.h> +#include <QtScript/QScriptContextInfo> +#include <QtCore/QDebug> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +/*! + Constructs a new agent for the given \a engine. The agent will + report debugging-related events (e.g. step completion) to the given + \a backend. +*/ +JSDebuggerAgent::JSDebuggerAgent(QScriptEngine *engine) + : QDeclarativeDebugService("JSDebugger"), QScriptEngineAgent(engine) +{} + +/*! + Destroys this QScriptDebuggerAgent. +*/ +JSDebuggerAgent::~JSDebuggerAgent() +{} + +/*! + \reimp +*/ +void JSDebuggerAgent::scriptLoad(qint64 id, const QString & program, + const QString &fileName, int ) +{ + filenames.insert(id, QUrl(fileName).toLocalFile()); + programs.insert(id, program); +} + +/*! + \reimp +*/ +void JSDebuggerAgent::scriptUnload(qint64 id) +{ + filenames.remove(id); +} + +/*! + \reimp +*/ +void JSDebuggerAgent::contextPush() +{ +} + +/*! + \reimp +*/ +void JSDebuggerAgent::contextPop() +{ +} + +/*! + \reimp +*/ +void JSDebuggerAgent::functionEntry(qint64 scriptId) +{ + Q_UNUSED(scriptId); + stepDepth++; +} + +/*! + \reimp +*/ +void JSDebuggerAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue) +{ + Q_UNUSED(scriptId); + Q_UNUSED(returnValue); + stepDepth--; +} + +/*! + \reimp +*/ +void JSDebuggerAgent::positionChange(qint64 scriptId, + int lineNumber, int columnNumber) +{ + Q_UNUSED(columnNumber); + + if(state == Stopped) + return; //no re-entrency + + // check breakpoints + if (!breakpointList.isEmpty()) { + QHash<qint64, QString>::const_iterator it = filenames.constFind(scriptId); + if (it != filenames.constEnd()) { + QPair<QString, qint32> key = qMakePair(*it, lineNumber); + if (breakpointList.contains(key)) { + stopped(); + return; + } + } + } + + + switch (state) { + case NoState: + case Stopped: + // Do nothing + break; + + case SteppingOutState: + if (stepDepth >= 0) + break; + //fallthough + case SteppingOverState: + if (stepDepth > 0) + break; + //fallthough + case SteppingIntoState: + qDebug() << programs.value(scriptId); + stopped(); + break; + } + +} + +/*! + \reimp +*/ +void JSDebuggerAgent::exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); + Q_UNUSED(hasHandler); +/* ... */ +} + +/*! + \reimp +*/ +void JSDebuggerAgent::exceptionCatch(qint64 scriptId, + const QScriptValue &exception) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); +} + +void JSDebuggerAgent::messageReceived(const QByteArray& message) +{ + QDataStream ds(message); + QByteArray command; + ds >> command; + if (command == "BREAKPOINTS") { + ds >> breakpointList; + } else if (command == "WATCH_EXPRESSIONS") { + ds >> watchExpressions; + } else if (command == "STEPOVER") { + stepDepth = 0; + state = SteppingOverState; + continueExec(); + } else if (command == "STEPINTO" || command == "INTERRUPT") { + stepDepth = 0; + state = SteppingIntoState; + continueExec(); + } else if (command == "STEPOUT") { + stepDepth = 0; + state = SteppingOutState; + continueExec(); + } else if (command == "CONTINUE") { + state = NoState; + continueExec(); + } else if (command == "EXEC") { + State oldState = state; + state = Stopped; + QByteArray id; + QString expr; + ds >> id >> expr; + + QVariant val = engine()->evaluate(expr).toVariant(); + // Clear any exceptions occurred during locals evaluation. + engine()->clearExceptions(); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("RESULT") << id << expr << val; + sendMessage(reply); + state = oldState; + + } else { + qDebug() << Q_FUNC_INFO << "Unknown command" << command; + } + + QDeclarativeDebugService::messageReceived(message); +} + +void JSDebuggerAgent::stopped() +{ + state = Stopped; + QList<QPair<QString, QPair<QString, qint32> > > backtrace; + + for (QScriptContext* ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) { + QScriptContextInfo info(ctx); + + QString functionName = info.functionName(); + if (functionName.isEmpty()) { + if (ctx->parentContext()) { + switch (info.functionType()) { + case QScriptContextInfo::ScriptFunction: + functionName = QLatin1String("<anonymous>"); + break; + case QScriptContextInfo::NativeFunction: + functionName = QLatin1String("<native>"); + break; + case QScriptContextInfo::QtFunction: + case QScriptContextInfo::QtPropertyFunction: + functionName = QLatin1String("<native slot>"); + break; + } + } else { + functionName = QLatin1String("<global>"); + } + } + backtrace.append(qMakePair(functionName, qMakePair( QUrl(info.fileName()).toLocalFile(), info.lineNumber() ) ) ); + } + QList<QPair<QString, QVariant> > watches; + foreach (const QString &expr, watchExpressions) { + watches << qMakePair(expr, engine()->evaluate(expr).toVariant()); + } + + // Clear any exceptions occurred during locals evaluation. + engine()->clearExceptions(); + + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("STOPPED") << backtrace << watches; + sendMessage(reply); + + loop.exec(QEventLoop::ExcludeUserInputEvents); +} + +void JSDebuggerAgent::continueExec() +{ + loop.quit(); +} + +void JSDebuggerAgent::enabledChanged(bool on) +{ + engine()->setAgent(on ? this : 0); + QDeclarativeDebugService::enabledChanged(on); +} + +QT_END_NAMESPACE diff --git a/src/tools/qml/qmlviewer/jsdebuggeragent.h b/src/tools/qml/qmlviewer/jsdebuggeragent.h new file mode 100644 index 0000000000000000000000000000000000000000..44002b9b5a7d193a4ed342c50c41e1537a4ddb90 --- /dev/null +++ b/src/tools/qml/qmlviewer/jsdebuggeragent.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSCriptTools module 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTDEBUGGERAGENT_P_H +#define QSCRIPTDEBUGGERAGENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtScript/qscriptengineagent.h> +#include <QtCore/QEventLoop> +#include <QtCore/QSet> +#include <private/qdeclarativedebugservice_p.h> +#include <QtCore/QStringList> + + +QT_BEGIN_NAMESPACE + +class JSDebuggerAgent : public QDeclarativeDebugService , public QScriptEngineAgent +{ Q_OBJECT +public: + JSDebuggerAgent(QScriptEngine *engine); + ~JSDebuggerAgent(); + + // reimplemented + void scriptLoad(qint64 id, const QString &program, + const QString &fileName, int baseLineNumber); + void scriptUnload(qint64 id); + + void contextPush(); + void contextPop(); + + void functionEntry(qint64 scriptId); + void functionExit(qint64 scriptId, + const QScriptValue &returnValue); + + void positionChange(qint64 scriptId, + int lineNumber, int columnNumber); + + void exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler); + void exceptionCatch(qint64 scriptId, + const QScriptValue &exception); + +/* bool supportsExtension(Extension extension) const; + QVariant extension(Extension extension, + const QVariant &argument = QVariant());*/ + + void messageReceived(const QByteArray &); + void enabledChanged(bool); + +public slots: +// void pauses(); + +private: + enum State { + NoState, + SteppingIntoState, + SteppingOverState, + SteppingOutState, + Stopped + }; + State state; + int stepDepth; + int stepCount; + + void continueExec(); + void stopped(); + + QEventLoop loop; + QHash <qint64, QString> filenames; + QHash <qint64, QString> programs; + QSet< QPair<QString, qint32> > breakpointList; + QStringList watchExpressions; + + Q_DISABLE_COPY(JSDebuggerAgent) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qml/qmlviewer/main.cpp b/src/tools/qml/qmlviewer/main.cpp index 4c410eeb3127c658d7fbe8591557381e6e11e21c..b57e6093f7d60dbe06501ace804c6772d3996ef7 100644 --- a/src/tools/qml/qmlviewer/main.cpp +++ b/src/tools/qml/qmlviewer/main.cpp @@ -226,6 +226,7 @@ int main(int argc, char ** argv) bool useNativeFileBrowser = true; bool experimentalGestures = false; bool designModeBehavior = false; + bool debuggerModeBehavior = false; WarningsConfig warningsConfig = DefaultWarnings; bool sizeToView = true; @@ -332,6 +333,8 @@ int main(int argc, char ** argv) experimentalGestures = true; } else if (arg == "-designmode") { designModeBehavior = true; + } else if (arg == "-debugger") { + debuggerModeBehavior = true; } else if (arg[0] != '-') { fileName = arg; } else if (1 || arg == "-help") { @@ -406,6 +409,7 @@ int main(int argc, char ** argv) viewer->enableExperimentalGestures(); viewer->setDesignModeBehavior(designModeBehavior); + viewer->setDebugMode(debuggerModeBehavior); foreach (QString lib, imports) viewer->addLibraryPath(lib); diff --git a/src/tools/qml/qmlviewer/qml.pri b/src/tools/qml/qmlviewer/qml.pri index bb12d683f51da30e34a55985cd2d899cc396d9b1..dff02fe71383fff1d342327b0b282ab17282f635 100644 --- a/src/tools/qml/qmlviewer/qml.pri +++ b/src/tools/qml/qmlviewer/qml.pri @@ -14,14 +14,17 @@ HEADERS += $$PWD/qmlruntime.h \ $$PWD/deviceorientation.h \ $$PWD/loggerwidget.h \ $$PWD/qdeclarativedesigndebugserver.h \ - $$PWD/qdeclarativedesignview.h + $$PWD/qdeclarativedesignview.h \ + $$PWD/jsdebuggeragent.h + SOURCES += $$PWD/qmlruntime.cpp \ $$PWD/proxysettings.cpp \ $$PWD/qdeclarativetester.cpp \ $$PWD/loggerwidget.cpp \ $$PWD/qdeclarativedesigndebugserver.cpp \ - $$PWD/qdeclarativedesignview.cpp + $$PWD/qdeclarativedesignview.cpp \ + $$PWD/jsdebuggeragent.cpp RESOURCES += $$PWD/qmlruntime.qrc diff --git a/src/tools/qml/qmlviewer/qmlruntime.cpp b/src/tools/qml/qmlviewer/qmlruntime.cpp index 9c09882322628c08f9176b1eb6d3b66066495bd0..0d725cf536c0298ee2bad0e8ca688439c35cf29a 100644 --- a/src/tools/qml/qmlviewer/qmlruntime.cpp +++ b/src/tools/qml/qmlviewer/qmlruntime.cpp @@ -62,6 +62,7 @@ #include "qdeclarative.h" #include <QAbstractAnimation> #include <private/qabstractanimation_p.h> +#include <private/qdeclarativeengine_p.h> #include <QSettings> #include <QXmlStreamReader> @@ -101,6 +102,7 @@ #endif #include <qdeclarativetester.h> +#include "jsdebuggeragent.h" QT_BEGIN_NAMESPACE @@ -634,6 +636,15 @@ void QDeclarativeViewer::setDesignModeBehavior(bool value) canvas->toolbar()->setEnabled(value); } +void QDeclarativeViewer::setDebugMode(bool on) +{ + //if (on) + { + new JSDebuggerAgent(QDeclarativeEnginePrivate::getScriptEngine(canvas->engine())); + } +} + + void QDeclarativeViewer::enableExperimentalGestures() { canvas->viewport()->grabGesture(Qt::TapGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); diff --git a/src/tools/qml/qmlviewer/qmlruntime.h b/src/tools/qml/qmlviewer/qmlruntime.h index 92dc09a0fe31a60751d72d4e60ea8bea119867db..f0d0e294399a90e48b7347f868397c054062438a 100644 --- a/src/tools/qml/qmlviewer/qmlruntime.h +++ b/src/tools/qml/qmlviewer/qmlruntime.h @@ -114,6 +114,7 @@ public: public slots: void setDesignModeBehavior(bool value); + void setDebugMode(bool on); void sceneResized(QSize size); bool open(const QString&); void openFile();