diff options
16 files changed, 658 insertions, 558 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro index 8d1a54e9e4..b0678ef2d9 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro +++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro @@ -12,9 +12,11 @@ SOURCES += \ $$PWD/qqmlnativedebugservice.cpp \ $$PWD/qqmlwatcher.cpp \ $$PWD/qv4debugservice.cpp \ + $$PWD/qv4debugger.cpp \ $$PWD/qv4debuggeragent.cpp \ $$PWD/qv4datacollector.cpp + HEADERS += \ $$PWD/../shared/qqmlconfigurabledebugservice.h \ $$PWD/qdebugmessageservice.h \ @@ -23,6 +25,7 @@ HEADERS += \ $$PWD/qqmlnativedebugservice.h \ $$PWD/qqmlwatcher.h \ $$PWD/qv4debugservice.h \ + $$PWD/qv4debugger.h \ $$PWD/qv4debuggeragent.h \ $$PWD/qv4datacollector.h diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 377f0845d0..e3347be14c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -40,6 +40,7 @@ #include <private/qv4runtime_p.h> #include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> QT_BEGIN_NAMESPACE @@ -208,13 +209,13 @@ QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) return ref; } -void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, - int frameNr, int scopeNr) +void QV4DataCollector::collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, + int scopeNr) { QStringList names; Refs refs; - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { + if (debugger->state() == QV4Debugger::Paused) { RefHolder holder(this, &refs); ArgumentCollectJob argumentsJob(m_engine, this, &names, frameNr, scopeNr); debugger->runInEngine(&argumentsJob); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index 0ea40f896c..bfa3bd2fe1 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -34,8 +34,9 @@ #ifndef QV4DATACOLLECTOR_H #define QV4DATACOLLECTOR_H +#include "qv4debugger.h" #include <private/qv4engine_p.h> -#include <private/qv4debugging_p.h> +#include <private/qv4persistent_p.h> QT_BEGIN_NAMESPACE @@ -60,8 +61,7 @@ public: Ref addFunctionRef(const QString &functionName); Ref addScriptRef(const QString &scriptName); - void collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, int frameNr, - int scopeNr); + void collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, int scopeNr); QV4::ExecutionEngine *engine() const { return m_engine; } @@ -101,7 +101,7 @@ private: QV4DataCollector::Refs *m_previousRefs; }; -class ExpressionEvalJob: public QV4::Debugging::V4Debugger::JavaScriptJob +class ExpressionEvalJob: public QV4Debugger::JavaScriptJob { QV4DataCollector *collector; QString exception; @@ -113,7 +113,7 @@ public: const QString &exceptionMessage() const; }; -class GatherSourcesJob: public QV4::Debugging::V4Debugger::Job +class GatherSourcesJob: public QV4Debugger::Job { QV4::ExecutionEngine *engine; QStringList sources; @@ -124,7 +124,7 @@ public: const QStringList &result() const; }; -class ArgumentCollectJob: public QV4::Debugging::V4Debugger::Job +class ArgumentCollectJob: public QV4Debugger::Job { QV4::ExecutionEngine *engine; QV4DataCollector *collector; @@ -138,7 +138,7 @@ public: void run(); }; -class LocalCollectJob: public QV4::Debugging::V4Debugger::Job +class LocalCollectJob: public QV4Debugger::Job { QV4::ExecutionEngine *engine; QV4DataCollector *collector; @@ -152,7 +152,7 @@ public: void run(); }; -class ThisCollectJob: public QV4::Debugging::V4Debugger::Job +class ThisCollectJob: public QV4Debugger::Job { QV4::ExecutionEngine *engine; QV4DataCollector *collector; @@ -166,7 +166,7 @@ public: bool myRun(); }; -class ExceptionCollectJob: public QV4::Debugging::V4Debugger::Job +class ExceptionCollectJob: public QV4Debugger::Job { QV4::ExecutionEngine *engine; QV4DataCollector *collector; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp new file mode 100644 index 0000000000..6198e2c039 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugger.h" + +#include <private/qv4scopedvalue_p.h> +#include <private/qv4script_p.h> + +QT_BEGIN_NAMESPACE + +QV4Debugger::BreakPoint::BreakPoint(const QString &fileName, int line) + : fileName(fileName), lineNumber(line) +{} + +inline uint qHash(const QV4Debugger::BreakPoint &b, uint seed = 0) Q_DECL_NOTHROW +{ + return qHash(b.fileName, seed) ^ b.lineNumber; +} + +inline bool operator==(const QV4Debugger::BreakPoint &a, + const QV4Debugger::BreakPoint &b) +{ + return a.lineNumber == b.lineNumber && a.fileName == b.fileName; +} + +QV4Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, + const QString &script) + : engine(engine) + , frameNr(frameNr) + , script(script) + , resultIsException(false) +{} + +void QV4Debugger::JavaScriptJob::run() +{ + QV4::Scope scope(engine); + + QV4::ExecutionContextSaver saver(scope); + + QV4::ExecutionContext *ctx = engine->currentContext; + if (frameNr > 0) { + for (int i = 0; i < frameNr; ++i) { + ctx = engine->parentContext(ctx); + } + engine->pushContext(ctx); + } + + QV4::Script script(ctx, this->script); + script.strictMode = ctx->d()->strictMode; + // In order for property lookups in QML to work, we need to disable fast v4 lookups. That + // is a side-effect of inheritContext. + script.inheritContext = true; + script.parse(); + QV4::ScopedValue result(scope); + if (!scope.engine->hasException) + result = script.run(); + if (scope.engine->hasException) { + result = scope.engine->catchException(); + resultIsException = true; + } + handleResult(result); +} + +bool QV4Debugger::JavaScriptJob::hasExeption() const +{ + return resultIsException; +} + +class EvalJob: public QV4Debugger::JavaScriptJob +{ + bool result; + +public: + EvalJob(QV4::ExecutionEngine *engine, const QString &script) + : QV4Debugger::JavaScriptJob(engine, /*frameNr*/-1, script) + , result(false) + {} + + virtual void handleResult(QV4::ScopedValue &result) + { + this->result = result->toBoolean(); + } + + bool resultAsBoolean() const + { + return result; + } +}; + +QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) + : m_engine(engine) + , m_state(Running) + , m_stepping(NotStepping) + , m_pauseRequested(false) + , m_haveBreakPoints(false) + , m_breakOnThrow(false) + , m_returnedValue(engine, QV4::Primitive::undefinedValue()) + , m_gatherSources(0) + , m_runningJob(0) +{ + static int debuggerId = qRegisterMetaType<QV4Debugger*>(); + static int pauseReasonId = qRegisterMetaType<QV4Debugger::PauseReason>(); + Q_UNUSED(debuggerId); + Q_UNUSED(pauseReasonId); +} + +QV4::ExecutionEngine *QV4Debugger::engine() const +{ + return m_engine; +} + +void QV4Debugger::pause() +{ + QMutexLocker locker(&m_lock); + if (m_state == Paused) + return; + m_pauseRequested = true; +} + +void QV4Debugger::resume(Speed speed) +{ + QMutexLocker locker(&m_lock); + if (m_state != Paused) + return; + + if (!m_returnedValue.isUndefined()) + m_returnedValue.set(m_engine, QV4::Encode::undefined()); + + m_currentContext.set(m_engine, *m_engine->currentContext); + m_stepping = speed; + m_runningCondition.wakeAll(); +} + +QV4Debugger::State QV4Debugger::state() const +{ + return m_state; +} + +void QV4Debugger::addBreakPoint(const QString &fileName, int lineNumber, const QString &condition) +{ + QMutexLocker locker(&m_lock); + m_breakPoints.insert(BreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), + lineNumber), condition); + m_haveBreakPoints = true; +} + +void QV4Debugger::removeBreakPoint(const QString &fileName, int lineNumber) +{ + QMutexLocker locker(&m_lock); + m_breakPoints.remove(BreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), + lineNumber)); + m_haveBreakPoints = !m_breakPoints.isEmpty(); +} + +void QV4Debugger::setBreakOnThrow(bool onoff) +{ + QMutexLocker locker(&m_lock); + + m_breakOnThrow = onoff; +} + +QV4Debugger::ExecutionState QV4Debugger::currentExecutionState() const +{ + ExecutionState state; + state.fileName = getFunction()->sourceFile(); + state.lineNumber = engine()->current->lineNumber; + + return state; +} + +bool QV4Debugger::pauseAtNextOpportunity() const { + return m_pauseRequested || m_haveBreakPoints || m_gatherSources || m_stepping >= StepOver; +} + +QVector<QV4::StackFrame> QV4Debugger::stackTrace(int frameLimit) const +{ + return m_engine->stackTrace(frameLimit); +} + +void QV4Debugger::maybeBreakAtInstruction() +{ + if (m_runningJob) // do not re-enter when we're doing a job for the debugger. + return; + + QMutexLocker locker(&m_lock); + + if (m_gatherSources) { + m_gatherSources->run(); + delete m_gatherSources; + m_gatherSources = 0; + } + + switch (m_stepping) { + case StepOver: + if (m_currentContext.asManaged()->d() != m_engine->current) + break; + // fall through + case StepIn: + pauseAndWait(Step); + return; + case StepOut: + case NotStepping: + break; + } + + if (m_pauseRequested) { // Serve debugging requests from the agent + m_pauseRequested = false; + pauseAndWait(PauseRequest); + } else if (m_haveBreakPoints) { + if (QV4::Function *f = getFunction()) { + const int lineNumber = engine()->current->lineNumber; + if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber)) + pauseAndWait(BreakPointHit); + } + } +} + +void QV4Debugger::enteringFunction() +{ + if (m_runningJob) + return; + QMutexLocker locker(&m_lock); + + if (m_stepping == StepIn) { + m_currentContext.set(m_engine, *m_engine->currentContext); + } +} + +void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal) +{ + if (m_runningJob) + return; + Q_UNUSED(retVal); // TODO + + QMutexLocker locker(&m_lock); + + if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { + m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); + m_stepping = StepOver; + m_returnedValue.set(m_engine, retVal); + } +} + +void QV4Debugger::aboutToThrow() +{ + if (!m_breakOnThrow) + return; + + if (m_runningJob) // do not re-enter when we're doing a job for the debugger. + return; + + QMutexLocker locker(&m_lock); + pauseAndWait(Throwing); +} + +QV4::Function *QV4Debugger::getFunction() const +{ + QV4::Scope scope(m_engine); + QV4::ExecutionContext *context = m_engine->currentContext; + QV4::ScopedFunctionObject function(scope, context->getFunctionObject()); + if (function) + return function->function(); + else + return context->d()->engine->globalCode; +} + +void QV4Debugger::pauseAndWait(PauseReason reason) +{ + if (m_runningJob) + return; + + m_state = Paused; + emit debuggerPaused(this, reason); + + while (true) { + m_runningCondition.wait(&m_lock); + if (m_runningJob) { + m_runningJob->run(); + m_jobIsRunning.wakeAll(); + } else { + break; + } + } + + m_state = Running; +} + +bool QV4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) +{ + QHash<BreakPoint, QString>::iterator it = m_breakPoints.find( + BreakPoint(filename.mid(filename.lastIndexOf('/') + 1), linenr)); + if (it == m_breakPoints.end()) + return false; + QString condition = it.value(); + if (condition.isEmpty()) + return true; + + Q_ASSERT(m_runningJob == 0); + EvalJob evilJob(m_engine, condition); + m_runningJob = &evilJob; + m_runningJob->run(); + m_runningJob = 0; + + return evilJob.resultAsBoolean(); +} + +void QV4Debugger::runInEngine(QV4Debugger::Job *job) +{ + QMutexLocker locker(&m_lock); + runInEngine_havingLock(job); +} + +void QV4Debugger::runInEngine_havingLock(QV4Debugger::Job *job) +{ + Q_ASSERT(job); + Q_ASSERT(m_runningJob == 0); + + m_runningJob = job; + m_runningCondition.wakeAll(); + m_jobIsRunning.wait(&m_lock); + m_runningJob = 0; +} + +QV4Debugger::Job::~Job() +{ +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h new file mode 100644 index 0000000000..adc58141d0 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGGER_H +#define QV4DEBUGGER_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 <private/qv4debugging_p.h> +#include <private/qv4function_p.h> +#include <private/qv4context_p.h> +#include <private/qv4persistent_p.h> + +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> + +QT_BEGIN_NAMESPACE + +class QV4Debugger : public QV4::Debugging::Debugger +{ + Q_OBJECT +public: + struct BreakPoint { + BreakPoint(const QString &fileName, int line); + QString fileName; + int lineNumber; + }; + + class Job + { + public: + virtual ~Job(); + virtual void run() = 0; + }; + + class JavaScriptJob: public Job + { + QV4::ExecutionEngine *engine; + int frameNr; + const QString &script; + bool resultIsException; + + public: + JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script); + void run(); + bool hasExeption() const; + + protected: + virtual void handleResult(QV4::ScopedValue &result) = 0; + }; + + enum State { + Running, + Paused + }; + + enum Speed { + FullThrottle = 0, + StepOut, + StepOver, + StepIn, + + NotStepping = FullThrottle + }; + + enum PauseReason { + PauseRequest, + BreakPointHit, + Throwing, + Step + }; + + QV4Debugger(QV4::ExecutionEngine *engine); + + QV4::ExecutionEngine *engine() const; + + void pause(); + void resume(Speed speed); + + State state() const; + + void addBreakPoint(const QString &fileName, int lineNumber, + const QString &condition = QString()); + void removeBreakPoint(const QString &fileName, int lineNumber); + + void setBreakOnThrow(bool onoff); + + // used for testing + struct ExecutionState + { + QString fileName; + int lineNumber; + }; + ExecutionState currentExecutionState() const; + + QVector<QV4::StackFrame> stackTrace(int frameLimit = -1) const; + QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const; + + QV4::Function *getFunction() const; + void runInEngine(Job *job); + + // compile-time interface + void maybeBreakAtInstruction() Q_DECL_OVERRIDE; + + // execution hooks + void enteringFunction() Q_DECL_OVERRIDE; + void leavingFunction(const QV4::ReturnedValue &retVal) Q_DECL_OVERRIDE; + void aboutToThrow() Q_DECL_OVERRIDE; + + bool pauseAtNextOpportunity() const Q_DECL_OVERRIDE; + +signals: + void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason); + +private: + // requires lock to be held + void pauseAndWait(PauseReason reason); + bool reallyHitTheBreakPoint(const QString &filename, int linenr); + void runInEngine_havingLock(QV4Debugger::Job *job); + + QV4::ExecutionEngine *m_engine; + QV4::PersistentValue m_currentContext; + QMutex m_lock; + QWaitCondition m_runningCondition; + State m_state; + Speed m_stepping; + bool m_pauseRequested; + bool m_haveBreakPoints; + bool m_breakOnThrow; + + QHash<BreakPoint, QString> m_breakPoints; + QV4::PersistentValue m_returnedValue; + + Job *m_gatherSources; + Job *m_runningJob; + QWaitCondition m_jobIsRunning; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QV4Debugger::PauseReason) +Q_DECLARE_METATYPE(QV4Debugger*) + +#endif // QV4DEBUGGER_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index dcb40dd548..da43257b24 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -44,7 +44,7 @@ QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService) : m_breakOnThrow(false), m_debugService(debugService) {} -QV4::Debugging::V4Debugger *QV4DebuggerAgent::firstDebugger() const +QV4Debugger *QV4DebuggerAgent::firstDebugger() const { // Currently only 1 single engine is supported, so: if (m_debuggers.isEmpty()) @@ -56,14 +56,13 @@ QV4::Debugging::V4Debugger *QV4DebuggerAgent::firstDebugger() const bool QV4DebuggerAgent::isRunning() const { // Currently only 1 single engine is supported, so: - if (QV4::Debugging::V4Debugger *debugger = firstDebugger()) - return debugger->state() == QV4::Debugging::V4Debugger::Running; + if (QV4Debugger *debugger = firstDebugger()) + return debugger->state() == QV4Debugger::Running; else return false; } -void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::V4Debugger *debugger, - QV4::Debugging::PauseReason reason) +void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason) { Q_UNUSED(reason); @@ -73,9 +72,9 @@ void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::V4Debugger *debugger, event.insert(QStringLiteral("type"), QStringLiteral("event")); switch (reason) { - case QV4::Debugging::Step: - case QV4::Debugging::PauseRequest: - case QV4::Debugging::BreakPoint: { + case QV4Debugger::Step: + case QV4Debugger::PauseRequest: + case QV4Debugger::BreakPointHit: { event.insert(QStringLiteral("event"), QStringLiteral("break")); QVector<QV4::StackFrame> frames = debugger->stackTrace(1); if (frames.isEmpty()) @@ -92,7 +91,7 @@ void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::V4Debugger *debugger, body.insert(QStringLiteral("breakpoints"), breakPoints); script.insert(QStringLiteral("name"), topFrame.source); } break; - case QV4::Debugging::Throwing: + case QV4Debugger::Throwing: // TODO: complete this! event.insert(QStringLiteral("event"), QStringLiteral("exception")); break; @@ -105,7 +104,7 @@ void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::V4Debugger *debugger, m_debugService->send(event); } -void QV4DebuggerAgent::addDebugger(QV4::Debugging::V4Debugger *debugger) +void QV4DebuggerAgent::addDebugger(QV4Debugger *debugger) { Q_ASSERT(!m_debuggers.contains(debugger)); m_debuggers << debugger; @@ -116,57 +115,50 @@ void QV4DebuggerAgent::addDebugger(QV4::Debugging::V4Debugger *debugger) if (breakPoint.enabled) debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); - connect(debugger, SIGNAL(destroyed(QObject*)), - this, SLOT(handleDebuggerDeleted(QObject*))); - connect(debugger, - SIGNAL(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)), - this, SLOT(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)), + connect(debugger, &QObject::destroyed, this, &QV4DebuggerAgent::handleDebuggerDeleted); + connect(debugger, &QV4Debugger::debuggerPaused, this, &QV4DebuggerAgent::debuggerPaused, Qt::QueuedConnection); } -void QV4DebuggerAgent::removeDebugger(QV4::Debugging::V4Debugger *debugger) +void QV4DebuggerAgent::removeDebugger(QV4Debugger *debugger) { m_debuggers.removeAll(debugger); - disconnect(debugger, SIGNAL(destroyed(QObject*)), - this, SLOT(handleDebuggerDeleted(QObject*))); - disconnect(debugger, - SIGNAL(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)), - this, - SLOT(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason))); + disconnect(debugger, &QObject::destroyed, this, &QV4DebuggerAgent::handleDebuggerDeleted); + disconnect(debugger, &QV4Debugger::debuggerPaused, this, &QV4DebuggerAgent::debuggerPaused); } -const QList<QV4::Debugging::V4Debugger *> &QV4DebuggerAgent::debuggers() +const QList<QV4Debugger *> &QV4DebuggerAgent::debuggers() { return m_debuggers; } void QV4DebuggerAgent::handleDebuggerDeleted(QObject *debugger) { - m_debuggers.removeAll(static_cast<QV4::Debugging::V4Debugger *>(debugger)); + m_debuggers.removeAll(static_cast<QV4Debugger *>(debugger)); } -void QV4DebuggerAgent::pause(QV4::Debugging::V4Debugger *debugger) const +void QV4DebuggerAgent::pause(QV4Debugger *debugger) const { debugger->pause(); } void QV4DebuggerAgent::pauseAll() const { - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) + foreach (QV4Debugger *debugger, m_debuggers) pause(debugger); } void QV4DebuggerAgent::resumeAll() const { - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) - debugger->resume(QV4::Debugging::V4Debugger::FullThrottle); + foreach (QV4Debugger *debugger, m_debuggers) + if (debugger->state() == QV4Debugger::Paused) + debugger->resume(QV4Debugger::FullThrottle); } int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition) { if (enabled) - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) + foreach (QV4Debugger *debugger, m_debuggers) debugger->addBreakPoint(fileName, lineNumber, condition); int id = m_breakPoints.size(); @@ -183,7 +175,7 @@ void QV4DebuggerAgent::removeBreakPoint(int id) m_breakPoints.remove(id); if (breakPoint.enabled) - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) + foreach (QV4Debugger *debugger, m_debuggers) debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); } @@ -201,7 +193,7 @@ void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff) return; breakPoint.enabled = onoff; - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) { + foreach (QV4Debugger *debugger, m_debuggers) { if (onoff) debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); else @@ -224,7 +216,7 @@ void QV4DebuggerAgent::setBreakOnThrow(bool onoff) { if (onoff != m_breakOnThrow) { m_breakOnThrow = onoff; - foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) + foreach (QV4Debugger *debugger, m_debuggers) debugger->setBreakOnThrow(onoff); } } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h index 9f77a17b45..d133c6954b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -34,7 +34,7 @@ #ifndef QV4DEBUGGERAGENT_H #define QV4DEBUGGERAGENT_H -#include <private/qv4debugging_p.h> +#include "qv4debugger.h" QT_BEGIN_NAMESPACE @@ -46,14 +46,14 @@ class QV4DebuggerAgent : public QObject public: QV4DebuggerAgent(QV4DebugServiceImpl *m_debugService); - QV4::Debugging::V4Debugger *firstDebugger() const; + QV4Debugger *firstDebugger() const; bool isRunning() const; - void addDebugger(QV4::Debugging::V4Debugger *debugger); - void removeDebugger(QV4::Debugging::V4Debugger *debugger); - const QList<QV4::Debugging::V4Debugger *> &debuggers(); + void addDebugger(QV4Debugger *debugger); + void removeDebugger(QV4Debugger *debugger); + const QList<QV4Debugger *> &debuggers(); - void pause(QV4::Debugging::V4Debugger *debugger) const; + void pause(QV4Debugger *debugger) const; void pauseAll() const; void resumeAll() const; int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString()); @@ -66,11 +66,11 @@ public: void setBreakOnThrow(bool onoff); public slots: - void debuggerPaused(QV4::Debugging::V4Debugger *debugger, QV4::Debugging::PauseReason reason); + void debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason); void handleDebuggerDeleted(QObject *debugger); private: - QList<QV4::Debugging::V4Debugger *> m_debuggers; + QList<QV4Debugger *> m_debuggers; struct BreakPoint { QString fileName; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index f742502e2a..3a080be125 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -270,7 +270,7 @@ public: int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); // no idea what the bottom property is for, so we'll ignore it. - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); QJsonArray frameArray; QVector<QV4::StackFrame> frames = debugger->stackTrace(toFrame); @@ -307,7 +307,7 @@ public: const int frameNr = arguments.value(QStringLiteral("number")).toInt( debugService->selectedFrame()); - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); if (frameNr < 0 || frameNr >= frames.size()) { createErrorResponse(QStringLiteral("frame command has invalid frame number")); @@ -340,7 +340,7 @@ public: debugService->selectedFrame()); const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); if (frameNr < 0 || frameNr >= frames.size()) { createErrorResponse(QStringLiteral("scope command has invalid frame number")); @@ -398,10 +398,10 @@ public: // decypher the payload: QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); if (arguments.empty()) { - debugger->resume(QV4::Debugging::V4Debugger::FullThrottle); + debugger->resume(QV4Debugger::FullThrottle); } else { QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); @@ -410,11 +410,11 @@ public: qWarning() << "Step count other than 1 is not supported."; if (stepAction == QStringLiteral("in")) { - debugger->resume(QV4::Debugging::V4Debugger::StepIn); + debugger->resume(QV4Debugger::StepIn); } else if (stepAction == QStringLiteral("out")) { - debugger->resume(QV4::Debugging::V4Debugger::StepOut); + debugger->resume(QV4Debugger::StepOut); } else if (stepAction == QStringLiteral("next")) { - debugger->resume(QV4::Debugging::V4Debugger::StepOver); + debugger->resume(QV4Debugger::StepOver); } else { createErrorResponse(QStringLiteral("continue command has invalid stepaction")); return; @@ -506,7 +506,7 @@ public: } // do it: - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); GatherSourcesJob job(debugger->engine()); debugger->runInEngine(&job); @@ -561,8 +561,8 @@ public: virtual void handleRequest() { - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { + QV4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + if (debugger->state() == QV4Debugger::Paused) { QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); QString expression = arguments.value(QStringLiteral("expression")).toString(); const int frame = arguments.value(QStringLiteral("frame")).toInt(0); @@ -634,7 +634,7 @@ void QV4DebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) { if (ee) { ee->iselFactory.reset(new QV4::Moth::ISelFactory); - QV4::Debugging::V4Debugger *debugger = new QV4::Debugging::V4Debugger(ee); + QV4Debugger *debugger = new QV4Debugger(ee); if (state() == Enabled) ee->setDebugger(debugger); debuggerAgent.addDebugger(debugger); @@ -651,8 +651,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine) if (engine){ const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); if (ee) { - QV4::Debugging::V4Debugger *debugger - = qobject_cast<QV4::Debugging::V4Debugger *>(ee->debugger); + QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger); if (debugger) debuggerAgent.removeDebugger(debugger); } @@ -664,7 +663,7 @@ void QV4DebugServiceImpl::stateAboutToBeChanged(State state) { QMutexLocker lock(&m_configMutex); if (state == Enabled) { - foreach (QV4::Debugging::V4Debugger *debugger, debuggerAgent.debuggers()) { + foreach (QV4Debugger *debugger, debuggerAgent.debuggers()) { QV4::ExecutionEngine *ee = debugger->engine(); if (!ee->debugger) ee->setDebugger(debugger); @@ -787,7 +786,7 @@ void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine) } QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::V4Debugger *debugger) + QV4Debugger *debugger) { QV4DataCollector::Ref ref; @@ -805,7 +804,7 @@ QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, i frame[QLatin1String("column")] = stackFrame.column; QJsonArray scopes; - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { + if (debugger->state() == QV4Debugger::Paused) { RefHolder holder(theCollector.data(), &collectedRefs); bool foundThis = false; ThisCollectJob job(debugger->engine(), theCollector.data(), frameNr, &foundThis); @@ -854,8 +853,7 @@ int QV4DebugServiceImpl::encodeScopeType(QV4::Heap::ExecutionContext::ContextTyp } } -QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, - QV4::Debugging::V4Debugger *debugger) +QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, QV4Debugger *debugger) { QJsonObject scope; @@ -863,7 +861,7 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, RefHolder holder(theCollector.data(), &collectedRefs); theCollector->collectScope(&object, debugger, frameNr, scopeNr); - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { + if (debugger->state() == QV4Debugger::Paused) { QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = QV4DataCollector::getScopeTypes(debugger->engine(), frameNr); scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index ea4a695fed..37b9f6f976 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -78,13 +78,12 @@ public: void signalEmitted(const QString &signal); void send(QJsonObject v8Payload); - QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::V4Debugger *debugger); + QJsonObject buildScope(int frameNr, int scopeNr, QV4Debugger *debugger); QJsonArray buildRefs(); QJsonValue lookup(QV4DataCollector::Ref refId); QJsonValue toRef(QV4DataCollector::Ref ref); - QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::V4Debugger *debugger); + QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, QV4Debugger *debugger); int selectedFrame() const; void selectFrame(int frameNr); diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index 7706a40da6..b04bcd33e7 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -51,290 +51,4 @@ QT_BEGIN_NAMESPACE -using namespace QV4; -using namespace QV4::Debugging; - -V4Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, - const QString &script) - : engine(engine) - , frameNr(frameNr) - , script(script) - , resultIsException(false) -{} - -void V4Debugger::JavaScriptJob::run() -{ - Scope scope(engine); - - ExecutionContextSaver saver(scope); - - ExecutionContext *ctx = engine->currentContext; - if (frameNr > 0) { - for (int i = 0; i < frameNr; ++i) { - ctx = engine->parentContext(ctx); - } - engine->pushContext(ctx); - } - - QV4::Script script(ctx, this->script); - script.strictMode = ctx->d()->strictMode; - // In order for property lookups in QML to work, we need to disable fast v4 lookups. That - // is a side-effect of inheritContext. - script.inheritContext = true; - script.parse(); - QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = script.run(); - if (scope.engine->hasException) { - result = scope.engine->catchException(); - resultIsException = true; - } - handleResult(result); -} - -bool V4Debugger::JavaScriptJob::hasExeption() const -{ - return resultIsException; -} - -class EvalJob: public V4Debugger::JavaScriptJob -{ - bool result; - -public: - EvalJob(QV4::ExecutionEngine *engine, const QString &script) - : V4Debugger::JavaScriptJob(engine, /*frameNr*/-1, script) - , result(false) - {} - - virtual void handleResult(QV4::ScopedValue &result) - { - this->result = result->toBoolean(); - } - - bool resultAsBoolean() const - { - return result; - } -}; - -V4Debugger::V4Debugger(QV4::ExecutionEngine *engine) - : m_engine(engine) - , m_state(Running) - , m_stepping(NotStepping) - , m_pauseRequested(false) - , m_haveBreakPoints(false) - , m_breakOnThrow(false) - , m_returnedValue(engine, Primitive::undefinedValue()) - , m_gatherSources(0) - , m_runningJob(0) -{ - qMetaTypeId<V4Debugger*>(); - qMetaTypeId<PauseReason>(); -} - -void V4Debugger::pause() -{ - QMutexLocker locker(&m_lock); - if (m_state == Paused) - return; - m_pauseRequested = true; -} - -void V4Debugger::resume(Speed speed) -{ - QMutexLocker locker(&m_lock); - if (m_state != Paused) - return; - - if (!m_returnedValue.isUndefined()) - m_returnedValue.set(m_engine, Encode::undefined()); - - m_currentContext.set(m_engine, *m_engine->currentContext); - m_stepping = speed; - m_runningCondition.wakeAll(); -} - -void V4Debugger::addBreakPoint(const QString &fileName, int lineNumber, const QString &condition) -{ - QMutexLocker locker(&m_lock); - m_breakPoints.insert(DebuggerBreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), lineNumber), condition); - m_haveBreakPoints = true; -} - -void V4Debugger::removeBreakPoint(const QString &fileName, int lineNumber) -{ - QMutexLocker locker(&m_lock); - m_breakPoints.remove(DebuggerBreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), lineNumber)); - m_haveBreakPoints = !m_breakPoints.isEmpty(); -} - -void V4Debugger::setBreakOnThrow(bool onoff) -{ - QMutexLocker locker(&m_lock); - - m_breakOnThrow = onoff; -} - -V4Debugger::ExecutionState V4Debugger::currentExecutionState() const -{ - ExecutionState state; - state.fileName = getFunction()->sourceFile(); - state.lineNumber = engine()->current->lineNumber; - - return state; -} - -QVector<StackFrame> V4Debugger::stackTrace(int frameLimit) const -{ - return m_engine->stackTrace(frameLimit); -} - -void V4Debugger::maybeBreakAtInstruction() -{ - if (m_runningJob) // do not re-enter when we're doing a job for the debugger. - return; - - QMutexLocker locker(&m_lock); - - if (m_gatherSources) { - m_gatherSources->run(); - delete m_gatherSources; - m_gatherSources = 0; - } - - switch (m_stepping) { - case StepOver: - if (m_currentContext.asManaged()->d() != m_engine->current) - break; - // fall through - case StepIn: - pauseAndWait(Step); - return; - case StepOut: - case NotStepping: - break; - } - - if (m_pauseRequested) { // Serve debugging requests from the agent - m_pauseRequested = false; - pauseAndWait(PauseRequest); - } else if (m_haveBreakPoints) { - if (Function *f = getFunction()) { - const int lineNumber = engine()->current->lineNumber; - if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber)) - pauseAndWait(BreakPoint); - } - } -} - -void V4Debugger::enteringFunction() -{ - if (m_runningJob) - return; - QMutexLocker locker(&m_lock); - - if (m_stepping == StepIn) { - m_currentContext.set(m_engine, *m_engine->currentContext); - } -} - -void V4Debugger::leavingFunction(const ReturnedValue &retVal) -{ - if (m_runningJob) - return; - Q_UNUSED(retVal); // TODO - - QMutexLocker locker(&m_lock); - - if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { - m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); - m_stepping = StepOver; - m_returnedValue.set(m_engine, retVal); - } -} - -void V4Debugger::aboutToThrow() -{ - if (!m_breakOnThrow) - return; - - if (m_runningJob) // do not re-enter when we're doing a job for the debugger. - return; - - QMutexLocker locker(&m_lock); - pauseAndWait(Throwing); -} - -Function *V4Debugger::getFunction() const -{ - Scope scope(m_engine); - ExecutionContext *context = m_engine->currentContext; - ScopedFunctionObject function(scope, context->getFunctionObject()); - if (function) - return function->function(); - else - return context->d()->engine->globalCode; -} - -void V4Debugger::pauseAndWait(PauseReason reason) -{ - if (m_runningJob) - return; - - m_state = Paused; - emit debuggerPaused(this, reason); - - while (true) { - m_runningCondition.wait(&m_lock); - if (m_runningJob) { - m_runningJob->run(); - m_jobIsRunning.wakeAll(); - } else { - break; - } - } - - m_state = Running; -} - -bool V4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) -{ - BreakPoints::iterator it = m_breakPoints.find(DebuggerBreakPoint(filename.mid(filename.lastIndexOf('/') + 1), linenr)); - if (it == m_breakPoints.end()) - return false; - QString condition = it.value(); - if (condition.isEmpty()) - return true; - - Q_ASSERT(m_runningJob == 0); - EvalJob evilJob(m_engine, condition); - m_runningJob = &evilJob; - m_runningJob->run(); - m_runningJob = 0; - - return evilJob.resultAsBoolean(); -} - -void V4Debugger::runInEngine(V4Debugger::Job *job) -{ - QMutexLocker locker(&m_lock); - runInEngine_havingLock(job); -} - -void V4Debugger::runInEngine_havingLock(V4Debugger::Job *job) -{ - Q_ASSERT(job); - Q_ASSERT(m_runningJob == 0); - - m_runningJob = job; - m_runningCondition.wakeAll(); - m_jobIsRunning.wait(&m_lock); - m_runningJob = 0; -} - -V4Debugger::Job::~Job() -{ -} - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 683036882e..3a3ecef918 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef DEBUGGING_H -#define DEBUGGING_H +#ifndef QV4DEBUGGING_H +#define QV4DEBUGGING_H // // W A R N I N G @@ -46,50 +46,13 @@ // #include "qv4global_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4scopedvalue_p.h" - -#include <QHash> -#include <QThread> -#include <QMutex> -#include <QWaitCondition> - -#include <QtCore/QJsonObject> +#include <QtCore/qobject.h> QT_BEGIN_NAMESPACE namespace QV4 { - -struct Function; - namespace Debugging { -enum PauseReason { - PauseRequest, - BreakPoint, - Throwing, - Step -}; - -struct DebuggerBreakPoint { - DebuggerBreakPoint(const QString &fileName, int line) - : fileName(fileName), lineNumber(line) - {} - QString fileName; - int lineNumber; -}; -inline uint qHash(const DebuggerBreakPoint &b, uint seed = 0) Q_DECL_NOTHROW -{ - return qHash(b.fileName, seed) ^ b.lineNumber; -} -inline bool operator==(const DebuggerBreakPoint &a, const DebuggerBreakPoint &b) -{ - return a.lineNumber == b.lineNumber && a.fileName == b.fileName; -} - -typedef QHash<DebuggerBreakPoint, QString> BreakPoints; - class Q_QML_EXPORT Debugger : public QObject { Q_OBJECT @@ -103,122 +66,9 @@ public: virtual void aboutToThrow() = 0; }; -class Q_QML_EXPORT V4Debugger : public Debugger -{ - Q_OBJECT -public: - class Q_QML_EXPORT Job - { - public: - virtual ~Job() = 0; - virtual void run() = 0; - }; - - class Q_QML_EXPORT JavaScriptJob: public Job - { - QV4::ExecutionEngine *engine; - int frameNr; - const QString &script; - bool resultIsException; - - public: - JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script); - void run(); - bool hasExeption() const; - - protected: - virtual void handleResult(QV4::ScopedValue &result) = 0; - }; - - enum State { - Running, - Paused - }; - - enum Speed { - FullThrottle = 0, - StepOut, - StepOver, - StepIn, - - NotStepping = FullThrottle - }; - - V4Debugger(ExecutionEngine *engine); - - ExecutionEngine *engine() const - { return m_engine; } - - void pause(); - void resume(Speed speed); - - State state() const { return m_state; } - - void addBreakPoint(const QString &fileName, int lineNumber, const QString &condition = QString()); - void removeBreakPoint(const QString &fileName, int lineNumber); - - void setBreakOnThrow(bool onoff); - - // used for testing - struct ExecutionState - { - QString fileName; - int lineNumber; - }; - ExecutionState currentExecutionState() const; - - bool pauseAtNextOpportunity() const { - return m_pauseRequested || m_haveBreakPoints || m_gatherSources || m_stepping >= StepOver; - } - - QVector<StackFrame> stackTrace(int frameLimit = -1) const; - QVector<Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const; - - Function *getFunction() const; - void runInEngine(Job *job); - -public: // compile-time interface - void maybeBreakAtInstruction(); - -public: // execution hooks - void enteringFunction(); - void leavingFunction(const ReturnedValue &retVal); - void aboutToThrow(); - -signals: - void debuggerPaused(QV4::Debugging::V4Debugger *self, QV4::Debugging::PauseReason reason); - -private: - // requires lock to be held - void pauseAndWait(PauseReason reason); - bool reallyHitTheBreakPoint(const QString &filename, int linenr); - void runInEngine_havingLock(V4Debugger::Job *job); - -private: - QV4::ExecutionEngine *m_engine; - QV4::PersistentValue m_currentContext; - QMutex m_lock; - QWaitCondition m_runningCondition; - State m_state; - Speed m_stepping; - bool m_pauseRequested; - bool m_haveBreakPoints; - bool m_breakOnThrow; - - BreakPoints m_breakPoints; - QV4::PersistentValue m_returnedValue; - - Job *m_gatherSources; - Job *m_runningJob; - QWaitCondition m_jobIsRunning; -}; - } // namespace Debugging } // namespace QV4 QT_END_NAMESPACE -Q_DECLARE_METATYPE(QV4::Debugging::Debugger*) -Q_DECLARE_METATYPE(QV4::Debugging::PauseReason) - -#endif // DEBUGGING_H +#endif // QV4DEBUGGING_H diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index 420510679f..ccb2d71c53 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -15,7 +15,8 @@ PUBLICTESTS += \ PRIVATETESTS += \ qqmldebugclient \ qqmldebuglocal \ - qqmldebugservice + qqmldebugservice \ + qv4debugger SUBDIRS += $$PUBLICTESTS diff --git a/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro b/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro new file mode 100644 index 0000000000..d11de56e43 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro @@ -0,0 +1,17 @@ +CONFIG += testcase +TARGET = tst_qv4debugger +osx:CONFIG -= app_bundle + +SOURCES += \ + $$PWD/tst_qv4debugger.cpp \ + $$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp \ + $$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp + +HEADERS += \ + $$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h \ + $$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h + +INCLUDEPATH += \ + $$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger + +QT += core-private gui-private qml-private network testlib diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 39a1fbc173..c5fa6be7a0 100644 --- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -33,6 +33,7 @@ #include <QtTest/QtTest> #include "qv4datacollector.h" +#include "qv4debugger.h" #include <QJSEngine> #include <QQmlEngine> @@ -159,7 +160,7 @@ public: } public slots: - void debuggerPaused(V4Debugger *debugger, QV4::Debugging::PauseReason reason) + void debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason) { Q_ASSERT(debugger == m_debugger); Q_ASSERT(debugger->engine() == collector.engine()); @@ -167,7 +168,7 @@ public slots: m_pauseReason = reason; m_statesWhenPaused << debugger->currentExecutionState(); - if (debugger->state() == V4Debugger::Paused && + if (debugger->state() == QV4Debugger::Paused && debugger->engine()->hasException) { Refs refs; RefHolder holder(&collector, &refs); @@ -184,7 +185,7 @@ public slots: m_stackTrace = debugger->stackTrace(); while (!m_expressionRequests.isEmpty()) { - Q_ASSERT(debugger->state() == V4Debugger::Paused); + Q_ASSERT(debugger->state() == QV4Debugger::Paused); ExpressionRequest request = m_expressionRequests.takeFirst(); m_expressionResults << Refs(); RefHolder holder(&collector, &m_expressionResults.last()); @@ -196,7 +197,7 @@ public slots: if (m_captureContextInfo) captureContextInfo(debugger); - debugger->resume(V4Debugger::FullThrottle); + debugger->resume(QV4Debugger::FullThrottle); } public: @@ -209,7 +210,7 @@ public: int lineNumber; }; - void captureContextInfo(V4Debugger *debugger) + void captureContextInfo(QV4Debugger *debugger) { for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) { m_capturedArguments.append(NamedRefs(&collector)); @@ -226,18 +227,17 @@ public: } } - void addDebugger(V4Debugger *debugger) + void addDebugger(QV4Debugger *debugger) { Q_ASSERT(!m_debugger); m_debugger = debugger; - connect(m_debugger, &V4Debugger::debuggerPaused, - this, &TestAgent::debuggerPaused); + connect(m_debugger, &QV4Debugger::debuggerPaused, this, &TestAgent::debuggerPaused); } bool m_wasPaused; - PauseReason m_pauseReason; + QV4Debugger::PauseReason m_pauseReason; bool m_captureContextInfo; - QList<V4Debugger::ExecutionState> m_statesWhenPaused; + QList<QV4Debugger::ExecutionState> m_statesWhenPaused; QList<TestBreakPoint> m_breakPointsToAddWhenPaused; QVector<QV4::StackFrame> m_stackTrace; QVector<NamedRefs> m_capturedArguments; @@ -251,7 +251,7 @@ public: }; QVector<ExpressionRequest> m_expressionRequests; QVector<Refs> m_expressionResults; - V4Debugger *m_debugger; + QV4Debugger *m_debugger; // Utility methods: void dumpStackTrace() const @@ -295,9 +295,9 @@ private slots: void evaluateExpression(); private: - V4Debugger *debugger() const + QV4Debugger *debugger() const { - return static_cast<V4Debugger *>(m_v4->debugger); + return static_cast<QV4Debugger *>(m_v4->debugger); } void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1) { @@ -319,7 +319,7 @@ void tst_qv4debugger::init() m_engine = new TestEngine; m_v4 = m_engine->v4Engine(); m_v4->iselFactory.reset(new QV4::Moth::ISelFactory); - m_v4->setDebugger(new V4Debugger(m_v4)); + m_v4->setDebugger(new QV4Debugger(m_v4)); m_engine->moveToThread(m_javaScriptThread); m_javaScriptThread->start(); m_debuggerAgent = new TestAgent(m_v4); @@ -359,7 +359,7 @@ void tst_qv4debugger::pendingBreakpoint() evaluateJavaScript(script, "testfile"); QVERIFY(m_debuggerAgent->m_wasPaused); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); QCOMPARE(state.fileName, QString("testfile")); QCOMPARE(state.lineNumber, 2); } @@ -375,7 +375,7 @@ void tst_qv4debugger::liveBreakPoint() evaluateJavaScript(script, "liveBreakPoint"); QVERIFY(m_debuggerAgent->m_wasPaused); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 2); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(1); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(1); QCOMPARE(state.fileName, QString("liveBreakPoint")); QCOMPARE(state.lineNumber, 3); } @@ -404,7 +404,7 @@ void tst_qv4debugger::addBreakPointWhilePaused() QVERIFY(m_debuggerAgent->m_wasPaused); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 2); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(0); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(0); QCOMPARE(state.fileName, QString("addBreakPointWhilePaused")); QCOMPARE(state.lineNumber, 1); @@ -415,7 +415,7 @@ void tst_qv4debugger::addBreakPointWhilePaused() static QV4::ReturnedValue someCall(QV4::CallContext *ctx) { - static_cast<V4Debugger *>(ctx->d()->engine->debugger) + static_cast<QV4Debugger *>(ctx->d()->engine->debugger) ->removeBreakPoint("removeBreakPointForNextInstruction", 2); return QV4::Encode::undefined(); } @@ -450,7 +450,7 @@ void tst_qv4debugger::conditionalBreakPoint() evaluateJavaScript(script, "conditionalBreakPoint"); QVERIFY(m_debuggerAgent->m_wasPaused); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 4); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); QCOMPARE(state.fileName, QString("conditionalBreakPoint")); QCOMPARE(state.lineNumber, 3); @@ -465,7 +465,7 @@ void tst_qv4debugger::conditionalBreakPointInQml() { QQmlEngine engine; QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); - V4Debugger *v4Debugger = new V4Debugger(v4); + QV4Debugger *v4Debugger = new QV4Debugger(v4); v4->iselFactory.reset(new QV4::Moth::ISelFactory); v4->setDebugger(v4Debugger); @@ -644,7 +644,7 @@ void tst_qv4debugger::pauseOnThrow() debugger()->setBreakOnThrow(true); evaluateJavaScript(script, "pauseOnThrow"); QVERIFY(m_debuggerAgent->m_wasPaused); - QCOMPARE(m_debuggerAgent->m_pauseReason, Throwing); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Throwing); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2); QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0)); QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue); @@ -665,9 +665,9 @@ void tst_qv4debugger::breakInCatch() debugger()->addBreakPoint("breakInCatch", 4); evaluateJavaScript(script, "breakInCatch"); QVERIFY(m_debuggerAgent->m_wasPaused); - QCOMPARE(m_debuggerAgent->m_pauseReason, BreakPoint); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::BreakPointHit); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); QCOMPARE(state.fileName, QString("breakInCatch")); QCOMPARE(state.lineNumber, 4); } @@ -682,9 +682,9 @@ void tst_qv4debugger::breakInWith() debugger()->addBreakPoint("breakInWith", 2); evaluateJavaScript(script, "breakInWith"); QVERIFY(m_debuggerAgent->m_wasPaused); - QCOMPARE(m_debuggerAgent->m_pauseReason, BreakPoint); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::BreakPointHit); QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1); - V4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first(); QCOMPARE(state.fileName, QString("breakInWith")); QCOMPARE(state.lineNumber, 2); } diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index b61eca730f..3a97bf655d 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -57,7 +57,6 @@ PRIVATETESTS += \ qrcqml \ qqmltimer \ qqmlinstantiator \ - qv4debugger \ qqmlenginecleanup \ v4misc \ qqmltranslation \ diff --git a/tests/auto/qml/qv4debugger/qv4debugger.pro b/tests/auto/qml/qv4debugger/qv4debugger.pro deleted file mode 100644 index 540cab70e6..0000000000 --- a/tests/auto/qml/qv4debugger/qv4debugger.pro +++ /dev/null @@ -1,15 +0,0 @@ -CONFIG += testcase -TARGET = tst_qv4debugger -macx:CONFIG -= app_bundle - -SOURCES += \ - $$PWD/tst_qv4debugger.cpp \ - $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp - -HEADERS += \ - $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h - -INCLUDEPATH += \ - $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger - -QT += core-private gui-private qml-private network testlib |