aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-07-24 10:29:04 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-07-26 11:53:42 +0200
commit07860794da5863610f38295c9d517fc457c5de95 (patch)
tree1ea8a4535985b90e8be80857ae086ee10eb15cb2
parent993bc84f49b4922480f6ec566f31c24465f0e005 (diff)
Initial support for debugging in the v4 interpreter
This adds breakpoint support to the Debugger, a helper function in the engine for enabling debugging (which will switch from JIT to the interpreter) and a DebuggingAgent interface, for use by v4 clients. Change-Id: I78e17a6cbe7196b0dfe4ee157fc028532131caa3 Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
-rw-r--r--src/qml/qml/v4/moth/qv4instr_moth_p.h6
-rw-r--r--src/qml/qml/v4/moth/qv4isel_moth.cpp5
-rw-r--r--src/qml/qml/v4/moth/qv4vme_moth.cpp34
-rw-r--r--src/qml/qml/v4/qv4codegen.cpp19
-rw-r--r--src/qml/qml/v4/qv4codegen_p.h4
-rw-r--r--src/qml/qml/v4/qv4debugging.cpp318
-rw-r--r--src/qml/qml/v4/qv4debugging_p.h154
-rw-r--r--src/qml/qml/v4/qv4engine.cpp14
-rw-r--r--src/qml/qml/v4/qv4engine_p.h6
-rw-r--r--src/qml/qml/v4/qv4isel_p.cpp3
-rw-r--r--src/qml/qml/v4/qv4script.cpp8
-rw-r--r--tests/auto/qml/qml.pro3
-rw-r--r--tests/auto/qml/qv4debugger/qv4debugger.pro7
-rw-r--r--tests/auto/qml/qv4debugger/tst_qv4debugger.cpp287
-rw-r--r--tools/v4/main.cpp14
15 files changed, 592 insertions, 290 deletions
diff --git a/src/qml/qml/v4/moth/qv4instr_moth_p.h b/src/qml/qml/v4/moth/qv4instr_moth_p.h
index 1ace0172f1..7397a1811d 100644
--- a/src/qml/qml/v4/moth/qv4instr_moth_p.h
+++ b/src/qml/qml/v4/moth/qv4instr_moth_p.h
@@ -112,9 +112,11 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1)
#ifdef MOTH_THREADED_INTERPRETER
-# define MOTH_INSTR_HEADER void *code;
+# define MOTH_INSTR_HEADER void *code; \
+ unsigned int breakPoint : 1;
#else
-# define MOTH_INSTR_HEADER quint8 instructionType;
+# define MOTH_INSTR_HEADER quint8 instructionType; \
+ unsigned int breakPoint : 1;
#endif
#define MOTH_INSTR_ENUM(I, FMT) I,
diff --git a/src/qml/qml/v4/moth/qv4isel_moth.cpp b/src/qml/qml/v4/moth/qv4isel_moth.cpp
index b4045b6dfb..04d759ed8d 100644
--- a/src/qml/qml/v4/moth/qv4isel_moth.cpp
+++ b/src/qml/qml/v4/moth/qv4isel_moth.cpp
@@ -214,6 +214,7 @@ void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *functi
int codeSize = 4096;
uchar *codeStart = new uchar[codeSize];
+ memset(codeStart, 0, codeSize);
uchar *codeNext = codeStart;
uchar *codeEnd = codeStart + codeSize;
@@ -278,6 +279,9 @@ void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *functi
_vmFunction->code = VME::exec;
_vmFunction->codeData = squeezeCode();
+ if (QV4::Debugging::Debugger *debugger = engine()->debugger)
+ debugger->setPendingBreakpoints(_vmFunction);
+
qSwap(_currentStatement, cs);
qSwap(_stackSlotAllocator, stackSlotAllocator);
delete stackSlotAllocator;
@@ -990,6 +994,7 @@ ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &in
#else
instr.common.instructionType = type;
#endif
+ instr.common.breakPoint = 0;
int instructionSize = Instr::size(type);
if (_codeEnd - _codeNext < instructionSize) {
diff --git a/src/qml/qml/v4/moth/qv4vme_moth.cpp b/src/qml/qml/v4/moth/qv4vme_moth.cpp
index 527e686592..5a292520eb 100644
--- a/src/qml/qml/v4/moth/qv4vme_moth.cpp
+++ b/src/qml/qml/v4/moth/qv4vme_moth.cpp
@@ -60,38 +60,11 @@
using namespace QQmlJS;
using namespace QQmlJS::Moth;
-class FunctionState: public Debugging::FunctionState
-{
-public:
- FunctionState(QV4::ExecutionContext *context, const uchar **code)
- : Debugging::FunctionState(context)
- , stack(0)
- , stackSize(0)
- , code(code)
- {
- previousInstructionPointer = context->interpreterInstructionPointer;
- context->interpreterInstructionPointer = code;
- }
- ~FunctionState()
- {
- context()->interpreterInstructionPointer = previousInstructionPointer;
- }
-
- virtual QV4::Value *temp(unsigned idx) { return stack + idx; }
-
- void setStack(QV4::Value *stack, unsigned stackSize)
- { this->stack = stack; this->stackSize = stackSize; }
-
-private:
- QV4::Value *stack;
- unsigned stackSize;
- const uchar **code;
- const uchar **previousInstructionPointer;
-};
-
#define MOTH_BEGIN_INSTR_COMMON(I) { \
const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \
code += InstrMeta<(int)Instr::I>::Size; \
+ if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \
+ context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \
Q_UNUSED(instr); \
TRACE_INSTR(I)
@@ -265,8 +238,6 @@ QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
}
#endif
- FunctionState state(context, &code);
-
#ifdef MOTH_THREADED_INTERPRETER
const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
goto *genericInstr->common.code;
@@ -320,7 +291,6 @@ QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
stackSize = instr.value;
stack = static_cast<QV4::Value *>(alloca(stackSize * sizeof(QV4::Value)));
memset(stack, 0, stackSize * sizeof(QV4::Value));
- state.setStack(stack, stackSize);
MOTH_END_INSTR(Push)
MOTH_BEGIN_INSTR(CallValue)
diff --git a/src/qml/qml/v4/qv4codegen.cpp b/src/qml/qml/v4/qv4codegen.cpp
index 804643687e..fb5eb128df 100644
--- a/src/qml/qml/v4/qv4codegen.cpp
+++ b/src/qml/qml/v4/qv4codegen.cpp
@@ -459,7 +459,6 @@ Codegen::Codegen(QV4::ExecutionContext *context, bool strict)
, _scopeAndFinally(0)
, _context(context)
, _strictMode(strict)
- , _debugger(context->engine->debugger)
, _errorHandler(0)
{
}
@@ -478,7 +477,6 @@ Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode)
, _scopeAndFinally(0)
, _context(0)
, _strictMode(strictMode)
- , _debugger(0)
, _errorHandler(errorHandler)
{
}
@@ -501,13 +499,6 @@ V4IR::Function *Codegen::operator()(const QString &fileName,
V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0,
node->elements, mode, inheritedLocals);
- if (_debugger) {
- if (node->elements->element) {
- SourceLocation loc = node->elements->element->firstSourceLocation();
- _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn);
- }
- }
-
qDeleteAll(_envMap);
_envMap.clear();
@@ -530,8 +521,6 @@ V4IR::Function *Codegen::operator()(const QString &fileName,
scan.leaveEnvironment();
V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
- if (_debugger)
- _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
qDeleteAll(_envMap);
_envMap.clear();
@@ -1410,8 +1399,6 @@ bool Codegen::visit(FieldMemberExpression *ast)
bool Codegen::visit(FunctionExpression *ast)
{
V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
- if (_debugger)
- _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
_expr.code = _block->CLOSURE(function);
return false;
}
@@ -1546,8 +1533,6 @@ bool Codegen::visit(ObjectLiteral *ast)
} else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
QString name = propertyName(gs->name);
V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0);
- if (_debugger)
- _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn);
ObjectPropertyValue &v = valueMap[name];
if (v.value ||
(gs->type == PropertyGetterSetter::Getter && v.getter) ||
@@ -1825,8 +1810,6 @@ V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
V4IR::Function *function = _module->newFunction(name, _function);
function->sourceFile = _fileName;
- if (_debugger)
- _debugger->addFunction(function);
V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock());
V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock);
V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock());
@@ -1899,8 +1882,6 @@ V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
if (member.function) {
V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
member.function->body ? member.function->body->elements : 0);
- if (_debugger)
- _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn);
if (! _env->parent) {
move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
_block->CLOSURE(function));
diff --git a/src/qml/qml/v4/qv4codegen_p.h b/src/qml/qml/v4/qv4codegen_p.h
index 484fa298b4..fe00d87852 100644
--- a/src/qml/qml/v4/qv4codegen_p.h
+++ b/src/qml/qml/v4/qv4codegen_p.h
@@ -59,9 +59,6 @@ namespace QQmlJS {
namespace AST {
class UiParameterList;
}
-namespace Debugging {
-class Debugger;
-} // namespace Debugging
@@ -442,7 +439,6 @@ private:
QHash<AST::FunctionExpression *, int> _functionMap;
QV4::ExecutionContext *_context;
bool _strictMode;
- Debugging::Debugger *_debugger;
ErrorHandler *_errorHandler;
class ScanFunctions;
diff --git a/src/qml/qml/v4/qv4debugging.cpp b/src/qml/qml/v4/qv4debugging.cpp
index 5a5503214c..624390d8f2 100644
--- a/src/qml/qml/v4/qv4debugging.cpp
+++ b/src/qml/qml/v4/qv4debugging.cpp
@@ -42,188 +42,163 @@
#include "qv4debugging_p.h"
#include "qv4object_p.h"
#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+#include "moth/qv4instr_moth_p.h"
#include <iostream>
-#define LOW_LEVEL_DEBUGGING_HELPERS
+using namespace QV4;
+using namespace QV4::Debugging;
-using namespace QQmlJS;
-using namespace QQmlJS::Debugging;
-
-FunctionState::FunctionState(QV4::ExecutionContext *context)
- : _context(context)
+Debugger::Debugger(QV4::ExecutionEngine *engine)
+ : _engine(engine)
+ , m_agent(0)
+ , m_state(Running)
+ , m_pauseRequested(false)
+ , m_currentInstructionPointer(0)
{
- if (debugger())
- debugger()->enterFunction(this);
+ qMetaTypeId<Debugger*>();
}
-FunctionState::~FunctionState()
+Debugger::~Debugger()
{
- if (debugger())
- debugger()->leaveFunction(this);
+ detachFromAgent();
}
-QV4::Value *FunctionState::argument(unsigned idx)
+void Debugger::attachToAgent(DebuggerAgent *agent)
{
- QV4::CallContext *c = _context->asCallContext();
- if (!c || idx >= c->argumentCount)
- return 0;
- return c->arguments + idx;
+ Q_ASSERT(!m_agent);
+ m_agent = agent;
}
-QV4::Value *FunctionState::local(unsigned idx)
+void Debugger::detachFromAgent()
{
- QV4::CallContext *c = _context->asCallContext();
- if (c && idx < c->variableCount())
- return c->locals + idx;
- return 0;
+ DebuggerAgent *agent = 0;
+ {
+ QMutexLocker locker(&m_lock);
+ agent = m_agent;
+ m_agent = 0;
+ }
+ if (agent)
+ agent->removeDebugger(this);
}
-#ifdef LOW_LEVEL_DEBUGGING_HELPERS
-Debugger *globalInstance = 0;
-
-void printStackTrace()
+void Debugger::pause()
{
- if (globalInstance)
- globalInstance->printStackTrace();
- else
- std::cerr << "No debugger." << std::endl;
+ QMutexLocker locker(&m_lock);
+ if (m_state == Paused)
+ return;
+ m_pauseRequested = true;
}
-#endif // DO_TRACE_INSTR
-Debugger::Debugger(QV4::ExecutionEngine *engine)
- : _engine(engine)
+void Debugger::resume()
{
-#ifdef LOW_LEVEL_DEBUGGING_HELPERS
- globalInstance = this;
-#endif // DO_TRACE_INSTR
+ QMutexLocker locker(&m_lock);
+ Q_ASSERT(m_state == Paused);
+ m_runningCondition.wakeAll();
}
-Debugger::~Debugger()
+void Debugger::addBreakPoint(const QString &fileName, int lineNumber)
{
-#ifdef LOW_LEVEL_DEBUGGING_HELPERS
- globalInstance = 0;
-#endif // DO_TRACE_INSTR
-
- qDeleteAll(_functionInfo.values());
+ QMutexLocker locker(&m_lock);
+ if (!m_pendingBreakPointsToRemove.remove(fileName, lineNumber))
+ m_pendingBreakPointsToAdd.add(fileName, lineNumber);
+ m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty();
}
-void Debugger::addFunction(V4IR::Function *function)
+void Debugger::removeBreakPoint(const QString &fileName, int lineNumber)
{
- _functionInfo.insert(function, new FunctionDebugInfo(function));
+ QMutexLocker locker(&m_lock);
+ if (!m_pendingBreakPointsToAdd.remove(fileName, lineNumber))
+ m_pendingBreakPointsToRemove.add(fileName, lineNumber);
+ m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty();
}
-void Debugger::setSourceLocation(V4IR::Function *function, unsigned line, unsigned column)
+Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) const
{
- _functionInfo[function]->setSourceLocation(line, column);
-}
+ if (!code)
+ code = m_currentInstructionPointer;
+ // ### Locking
+ ExecutionState state;
+
+ QV4::ExecutionContext *context = _engine->current;
+ QV4::Function *function = 0;
+ if (CallContext *callCtx = context->asCallContext())
+ function = callCtx->function->function;
+ else {
+ Q_ASSERT(context->type == QV4::ExecutionContext::Type_GlobalContext);
+ function = context->engine->globalCode;
+ }
-void Debugger::mapFunction(QV4::Function *vmf, V4IR::Function *irf)
-{
- _vmToIr.insert(vmf, irf);
-}
+ state.function = function;
+ state.fileName = function->sourceFile;
-FunctionDebugInfo *Debugger::debugInfo(QV4::FunctionObject *function) const
-{
- if (!function)
- return 0;
+ qptrdiff relativeProgramCounter = code - function->codeData;
+ state.lineNumber = function->lineNumberForProgramCounter(relativeProgramCounter);
- if (function->function)
- return _functionInfo[irFunction(function->function)];
- else
- return 0;
+ return state;
}
-QString Debugger::name(QV4::FunctionObject *function) const
+void Debugger::setPendingBreakpoints(Function *function)
{
- if (FunctionDebugInfo *i = debugInfo(function))
- return i->name;
-
- return QString();
+ m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false);
}
-void Debugger::aboutToCall(QV4::FunctionObject *function, QV4::ExecutionContext *context)
+void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit)
{
- _callStack.append(CallInfo(context, function));
-}
+ QMutexLocker locker(&m_lock);
+ m_currentInstructionPointer = code;
-void Debugger::justLeft(QV4::ExecutionContext *context)
-{
- int idx = callIndex(context);
- if (idx < 0)
- qDebug() << "Oops, leaving a function that was not registered...?";
- else
- _callStack.resize(idx);
-}
+ // Do debugger internal work
+ if (m_havePendingBreakPoints) {
-void Debugger::enterFunction(FunctionState *state)
-{
- _callStack[callIndex(state->context())].state = state;
-
-#ifdef DO_TRACE_INSTR
- QString n = name(_callStack[callIndex(state->context())].function);
- std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl;
-// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i)
-// std::cerr << " " << i << ": " << currentArg(i) << std::endl;
-#endif // DO_TRACE_INSTR
-}
+ if (breakPointHit) {
+ ExecutionState state = currentExecutionState();
+ breakPointHit = !m_pendingBreakPointsToRemove.contains(state.fileName, state.lineNumber);
+ }
-void Debugger::leaveFunction(FunctionState *state)
-{
- _callStack[callIndex(state->context())].state = 0;
-}
-
-void Debugger::aboutToThrow(const QV4::Value &value)
-{
- qDebug() << "*** We are about to throw...:" << value.toString(currentState()->context())->toQString();
-}
+ applyPendingBreakPoints();
+ }
-FunctionState *Debugger::currentState() const
-{
- if (_callStack.isEmpty())
- return 0;
- else
- return _callStack.last().state;
-}
+ // Serve debugging requests from the agent
+ if (m_pauseRequested) {
+ m_pauseRequested = false;
+ pauseAndWait();
+ } else if (breakPointHit)
+ pauseAndWait();
-const char *Debugger::currentArg(unsigned idx) const
-{
- FunctionState *state = currentState();
- return qPrintable(state->argument(idx)->toString(state->context())->toQString());
+ if (!m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty())
+ applyPendingBreakPoints();
}
-const char *Debugger::currentLocal(unsigned idx) const
+void Debugger::aboutToThrow(const QV4::Value &value)
{
- FunctionState *state = currentState();
- return qPrintable(state->local(idx)->toString(state->context())->toQString());
+ qDebug() << "*** We are about to throw...";
}
-const char *Debugger::currentTemp(unsigned idx) const
+void Debugger::pauseAndWait()
{
- FunctionState *state = currentState();
- return qPrintable(state->temp(idx)->toString(state->context())->toQString());
+ m_state = Paused;
+ QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, Q_ARG(QV4::Debugging::Debugger*, this));
+ m_runningCondition.wait(&m_lock);
+ m_state = Running;
}
-void Debugger::printStackTrace() const
+void Debugger::applyPendingBreakPoints()
{
- for (int i = _callStack.size() - 1; i >=0; --i) {
- QString n = name(_callStack[i].function);
- std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl;
+ foreach (Function *function, _engine->functions) {
+ m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false);
+ m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true);
}
-}
-int Debugger::callIndex(QV4::ExecutionContext *context)
-{
- for (int idx = _callStack.size() - 1; idx >= 0; --idx) {
- if (_callStack[idx].context == context)
- return idx;
+ for (BreakPoints::ConstIterator it = m_pendingBreakPointsToAdd.constBegin(),
+ end = m_pendingBreakPointsToAdd.constEnd(); it != end; ++it) {
+ foreach (int lineNumber, it.value())
+ m_pendingBreakPointsToAddToFutureCode.add(it.key(), lineNumber);
}
- return -1;
-}
-
-V4IR::Function *Debugger::irFunction(QV4::Function *vmf) const
-{
- return _vmToIr[vmf];
+ m_pendingBreakPointsToAdd.clear();
+ m_pendingBreakPointsToRemove.clear();
+ m_havePendingBreakPoints = false;
}
static void realDumpValue(QV4::Value v, QV4::ExecutionContext *ctx, std::string prefix)
@@ -306,3 +281,92 @@ void dumpValue(QV4::Value v, QV4::ExecutionContext *ctx)
{
realDumpValue(v, ctx, std::string(""));
}
+
+
+void DebuggerAgent::addDebugger(Debugger *debugger)
+{
+ Q_ASSERT(!m_debuggers.contains(debugger));
+ m_debuggers << debugger;
+ debugger->attachToAgent(this);
+}
+
+void DebuggerAgent::removeDebugger(Debugger *debugger)
+{
+ m_debuggers.removeAll(debugger);
+ debugger->detachFromAgent();
+}
+
+void DebuggerAgent::pause(Debugger *debugger)
+{
+ debugger->pause();
+}
+
+void DebuggerAgent::addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber)
+{
+ debugger->addBreakPoint(fileName, lineNumber);
+}
+
+void DebuggerAgent::removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber)
+{
+ debugger->removeBreakPoint(fileName, lineNumber);
+}
+
+DebuggerAgent::~DebuggerAgent()
+{
+ Q_ASSERT(m_debuggers.isEmpty());
+}
+
+void Debugger::BreakPoints::add(const QString &fileName, int lineNumber)
+{
+ QList<int> &lines = (*this)[fileName];
+ if (!lines.contains(lineNumber)) {
+ lines.append(lineNumber);
+ qSort(lines);
+ }
+}
+
+bool Debugger::BreakPoints::remove(const QString &fileName, int lineNumber)
+{
+ Iterator breakPoints = find(fileName);
+ if (breakPoints == constEnd())
+ return false;
+ return breakPoints->removeAll(lineNumber) > 0;
+}
+
+bool Debugger::BreakPoints::contains(const QString &fileName, int lineNumber) const
+{
+ ConstIterator breakPoints = find(fileName);
+ if (breakPoints == constEnd())
+ return false;
+ return breakPoints->contains(lineNumber);
+}
+
+void Debugger::BreakPoints::applyToFunction(Function *function, bool removeBreakPoints)
+{
+ Iterator breakPointsForFile = find(function->sourceFile);
+ if (breakPointsForFile == end())
+ return;
+
+ QList<int>::Iterator breakPoint = breakPointsForFile->begin();
+ while (breakPoint != breakPointsForFile->end()) {
+ bool breakPointFound = false;
+ for (QVector<LineNumberMapping>::ConstIterator mapping = function->lineNumberMappings.constBegin(),
+ end = function->lineNumberMappings.constEnd(); mapping != end; ++mapping) {
+ if (mapping->lineNumber == *breakPoint) {
+ uchar *codePtr = const_cast<uchar *>(function->codeData) + mapping->codeOffset;
+ QQmlJS::Moth::Instr *instruction = reinterpret_cast<QQmlJS::Moth::Instr*>(codePtr);
+ instruction->common.breakPoint = !removeBreakPoints;
+ // Continue setting the next break point.
+ breakPointFound = true;
+ break;
+ }
+ }
+ if (breakPointFound)
+ breakPoint = breakPointsForFile->erase(breakPoint);
+ else
+ ++breakPoint;
+ }
+
+ if (breakPointsForFile->isEmpty())
+ erase(breakPointsForFile);
+}
diff --git a/src/qml/qml/v4/qv4debugging_p.h b/src/qml/qml/v4/qv4debugging_p.h
index 51bac14927..4a273be732 100644
--- a/src/qml/qml/v4/qv4debugging_p.h
+++ b/src/qml/qml/v4/qv4debugging_p.h
@@ -45,113 +45,117 @@
#include "qv4global_p.h"
#include "qv4engine_p.h"
#include "qv4context_p.h"
+#include "qv4jsir_p.h"
#include <QHash>
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
QT_BEGIN_NAMESPACE
-namespace QQmlJS {
+namespace QV4 {
-namespace V4IR {
-struct BasicBlock;
struct Function;
-} // namespace IR
namespace Debugging {
-class Debugger;
+class DebuggerAgent;
-struct FunctionDebugInfo { // TODO: use opaque d-pointers here
- QString name;
- unsigned startLine, startColumn;
+class Q_QML_EXPORT Debugger
+{
+public:
+ enum State {
+ Running,
+ Paused
+ };
- FunctionDebugInfo(V4IR::Function *function):
- startLine(0), startColumn(0)
- {
- if (function->name)
- name = *function->name;
- }
+ Debugger(ExecutionEngine *_engine);
+ ~Debugger();
- void setSourceLocation(unsigned line, unsigned column)
- { startLine = line; startColumn = column; }
-};
+ void attachToAgent(DebuggerAgent *agent);
+ void detachFromAgent();
-class FunctionState
-{
-public:
- FunctionState(QV4::ExecutionContext *context);
- virtual ~FunctionState();
+ void pause();
+ void resume();
+
+ State state() const { return m_state; }
+
+ void addBreakPoint(const QString &fileName, int lineNumber);
+ void removeBreakPoint(const QString &fileName, int lineNumber);
+
+ struct ExecutionState
+ {
+ ExecutionState() : lineNumber(-1), function(0) {}
+ QString fileName;
+ int lineNumber;
+ Function *function;
+ };
- virtual QV4::Value *argument(unsigned idx);
- virtual QV4::Value *local(unsigned idx);
- virtual QV4::Value *temp(unsigned idx) = 0;
+ ExecutionState currentExecutionState(const uchar *code = 0) const;
- QV4::ExecutionContext *context() const
- { return _context; }
+ bool pauseAtNextOpportunity() const {
+ return m_pauseRequested || m_havePendingBreakPoints;
+ }
+ void setPendingBreakpoints(Function *function);
- Debugger *debugger() const
- { return _context->engine->debugger; }
+public: // compile-time interface
+ void maybeBreakAtInstruction(const uchar *code, bool breakPointHit);
+
+public: // execution hooks
+ void aboutToThrow(const Value &value);
private:
- QV4::ExecutionContext *_context;
-};
+ // requires lock to be held
+ void pauseAndWait();
-struct CallInfo
-{
- QV4::ExecutionContext *context;
- QV4::FunctionObject *function;
- FunctionState *state;
-
- CallInfo(QV4::ExecutionContext *context = 0, QV4::FunctionObject *function = 0, FunctionState *state = 0)
- : context(context)
- , function(function)
- , state(state)
- {}
+ void applyPendingBreakPoints();
+
+ struct BreakPoints : public QHash<QString, QList<int> >
+ {
+ void add(const QString &fileName, int lineNumber);
+ bool remove(const QString &fileName, int lineNumber);
+ bool contains(const QString &fileName, int lineNumber) const;
+ void applyToFunction(Function *function, bool removeBreakPoints);
+ };
+
+ QV4::ExecutionEngine *_engine;
+ DebuggerAgent *m_agent;
+ QMutex m_lock;
+ QWaitCondition m_runningCondition;
+ State m_state;
+ bool m_pauseRequested;
+ bool m_havePendingBreakPoints;
+ BreakPoints m_pendingBreakPointsToAdd;
+ BreakPoints m_pendingBreakPointsToAddToFutureCode;
+ BreakPoints m_pendingBreakPointsToRemove;
+ const uchar *m_currentInstructionPointer;
};
-class Q_QML_EXPORT Debugger
+class Q_QML_EXPORT DebuggerAgent : public QObject
{
+ Q_OBJECT
public:
- Debugger(QV4::ExecutionEngine *_engine);
- ~Debugger();
+ ~DebuggerAgent();
-public: // compile-time interface
- void addFunction(V4IR::Function *function);
- void setSourceLocation(V4IR::Function *function, unsigned line, unsigned column);
- void mapFunction(QV4::Function *vmf, V4IR::Function *irf);
+ void addDebugger(Debugger *debugger);
+ void removeDebugger(Debugger *debugger);
-public: // run-time querying interface
- FunctionDebugInfo *debugInfo(QV4::FunctionObject *function) const;
- QString name(QV4::FunctionObject *function) const;
+ void pause(Debugger *debugger);
+ void addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber);
+ void removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber);
-public: // execution hooks
- void aboutToCall(QV4::FunctionObject *function, QV4::ExecutionContext *context);
- void justLeft(QV4::ExecutionContext *context);
- void enterFunction(FunctionState *state);
- void leaveFunction(FunctionState *state);
- void aboutToThrow(const QV4::Value &value);
-
-public: // debugging hooks
- FunctionState *currentState() const;
- const char *currentArg(unsigned idx) const;
- const char *currentLocal(unsigned idx) const;
- const char *currentTemp(unsigned idx) const;
- void printStackTrace() const;
+ Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger) = 0;
-private:
- int callIndex(QV4::ExecutionContext *context);
- V4IR::Function *irFunction(QV4::Function *vmf) const;
-
-private: // TODO: use opaque d-pointers here
- QV4::ExecutionEngine *_engine;
- QHash<V4IR::Function *, FunctionDebugInfo *> _functionInfo;
- QHash<QV4::Function *, V4IR::Function *> _vmToIr;
- QVector<CallInfo> _callStack;
+protected:
+ QList<Debugger *> m_debuggers;
};
} // namespace Debugging
-} // namespace QQmlJS
+} // namespace QV4
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QV4::Debugging::Debugger*)
+
#endif // DEBUGGING_H
diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp
index 56c711ceb7..0b7c850aa6 100644
--- a/src/qml/qml/v4/qv4engine.cpp
+++ b/src/qml/qml/v4/qv4engine.cpp
@@ -68,11 +68,11 @@
#include "qv4stacktrace_p.h"
#ifdef V4_ENABLE_JIT
-# include "qv4isel_masm_p.h"
-#else // !V4_ENABLE_JIT
-# include "qv4isel_moth_p.h"
+#include "qv4isel_masm_p.h"
#endif // V4_ENABLE_JIT
+#include "qv4isel_moth_p.h"
+
QT_BEGIN_NAMESPACE
using namespace QV4;
@@ -270,6 +270,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
ExecutionEngine::~ExecutionEngine()
{
+ delete debugger;
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = 0;
delete memoryManager;
@@ -284,6 +285,13 @@ ExecutionEngine::~ExecutionEngine()
delete executableAllocator;
}
+void ExecutionEngine::enableDebugger()
+{
+ Q_ASSERT(!debugger);
+ debugger = new Debugging::Debugger(this);
+ iselFactory.reset(new QQmlJS::Moth::ISelFactory);
+}
+
void ExecutionEngine::initRootContext()
{
rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h
index 842fb44ecb..20fbf03ae2 100644
--- a/src/qml/qml/v4/qv4engine_p.h
+++ b/src/qml/qml/v4/qv4engine_p.h
@@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE
class QV8Engine;
-namespace QQmlJS {
+namespace QV4 {
namespace Debugging {
class Debugger;
} // namespace Debugging
@@ -121,7 +121,7 @@ struct Q_QML_EXPORT ExecutionEngine
IdentifierTable *identifierTable;
- QQmlJS::Debugging::Debugger *debugger;
+ QV4::Debugging::Debugger *debugger;
Object *globalObject;
@@ -227,6 +227,8 @@ struct Q_QML_EXPORT ExecutionEngine
ExecutionEngine(QQmlJS::EvalISelFactory *iselFactory = 0);
~ExecutionEngine();
+ void enableDebugger();
+
WithContext *newWithContext(Object *with);
CatchContext *newCatchContext(String* exceptionVarName, const QV4::Value &exceptionValue);
CallContext *newCallContext(FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc);
diff --git a/src/qml/qml/v4/qv4isel_p.cpp b/src/qml/qml/v4/qv4isel_p.cpp
index 99c363bd36..ca8d249f9f 100644
--- a/src/qml/qml/v4/qv4isel_p.cpp
+++ b/src/qml/qml/v4/qv4isel_p.cpp
@@ -103,9 +103,6 @@ QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *ou
foreach (V4IR::Function *function, irFunction->nestedFunctions)
createFunctionMapping(vmFunction, function);
- if (_engine->debugger)
- _engine->debugger->mapFunction(vmFunction, irFunction);
-
return vmFunction;
}
diff --git a/src/qml/qml/v4/qv4script.cpp b/src/qml/qml/v4/qv4script.cpp
index 1a6098b2ce..3de218a451 100644
--- a/src/qml/qml/v4/qv4script.cpp
+++ b/src/qml/qml/v4/qv4script.cpp
@@ -192,9 +192,6 @@ Value Script::run()
QV4::ExecutionEngine *engine = scope->engine;
- if (engine->debugger)
- engine->debugger->aboutToCall(0, scope);
-
if (qml.isEmpty()) {
TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
@@ -204,9 +201,6 @@ Value Script::run()
scope->strictMode = vmFunction->isStrict;
scope->lookups = vmFunction->lookups;
- if (engine->debugger)
- engine->debugger->aboutToCall(0, scope);
-
QV4::Value result;
try {
result = vmFunction->code(scope, vmFunction->codeData);
@@ -216,8 +210,6 @@ Value Script::run()
throw;
}
- if (engine->debugger)
- engine->debugger->justLeft(scope);
return result;
} else {
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 371d47ba32..18065f1a24 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -54,7 +54,8 @@ PRIVATETESTS += \
qqmlbundle \
qrcqml \
qqmltimer \
- qqmlinstantiator
+ qqmlinstantiator \
+ qv4debugger
qtHaveModule(widgets) {
PUBLICTESTS += \
diff --git a/tests/auto/qml/qv4debugger/qv4debugger.pro b/tests/auto/qml/qv4debugger/qv4debugger.pro
new file mode 100644
index 0000000000..2a318955f3
--- /dev/null
+++ b/tests/auto/qml/qv4debugger/qv4debugger.pro
@@ -0,0 +1,7 @@
+CONFIG += testcase
+TARGET = tst_qv4debugger
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qv4debugger.cpp
+
+QT += core-private gui-private qml-private network testlib
diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
new file mode 100644
index 0000000000..c6ecd1938a
--- /dev/null
+++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+
+#include <QJSEngine>
+#include <private/qv4engine_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv8engine_p.h>
+
+static bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000)
+{
+ QEventLoop loop;
+ QObject::connect(obj, signal, &loop, SLOT(quit()));
+ QTimer timer;
+ QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
+ if (timeout > 0) {
+ QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
+ timer.setSingleShot(true);
+ timer.start(timeout);
+ }
+ loop.exec();
+ return timeoutSpy.isEmpty();
+}
+
+class TestEngine : public QJSEngine
+{
+ Q_OBJECT
+public:
+ TestEngine()
+ {
+ qMetaTypeId<InjectedFunction>();
+ }
+
+ Q_INVOKABLE void evaluate(const QString &script, const QString &fileName, int lineNumber = 1)
+ {
+ QJSEngine::evaluate(script, fileName, lineNumber);
+ emit evaluateFinished();
+ }
+
+ QV4::ExecutionEngine *v4Engine() { return QV8Engine::getV4(this); }
+
+ typedef QV4::Value (*InjectedFunction)(QV4::SimpleCallContext*);
+
+ Q_INVOKABLE void injectFunction(const QString &functionName, TestEngine::InjectedFunction injectedFunction)
+ {
+ QV4::ExecutionEngine *v4 = v4Engine();
+
+ QV4::String *name = v4->newString(functionName);
+ QV4::Value function = QV4::Value::fromObject(v4->newBuiltinFunction(v4->rootContext, name, injectedFunction));
+ v4->globalObject->put(name, function);
+ }
+
+signals:
+ void evaluateFinished();
+};
+
+Q_DECLARE_METATYPE(TestEngine::InjectedFunction)
+
+class TestAgent : public QV4::Debugging::DebuggerAgent
+{
+ Q_OBJECT
+public:
+ TestAgent()
+ : m_wasPaused(false)
+ {
+ }
+
+ virtual void debuggerPaused(QV4::Debugging::Debugger *debugger)
+ {
+ Q_ASSERT(m_debuggers.count() == 1 && m_debuggers.first() == debugger);
+ m_wasPaused = true;
+ m_statesWhenPaused << debugger->currentExecutionState();
+
+ foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused)
+ debugger->addBreakPoint(bp.fileName, bp.lineNumber);
+ m_breakPointsToAddWhenPaused.clear();
+
+ debugger->resume();
+ }
+
+ int debuggerCount() const { return m_debuggers.count(); }
+
+ struct TestBreakPoint
+ {
+ TestBreakPoint() : lineNumber(-1) {}
+ TestBreakPoint(const QString &fileName, int lineNumber)
+ : fileName(fileName), lineNumber(lineNumber) {}
+ QString fileName;
+ int lineNumber;
+ };
+
+ bool m_wasPaused;
+ QList<QV4::Debugging::Debugger::ExecutionState> m_statesWhenPaused;
+ QList<TestBreakPoint> m_breakPointsToAddWhenPaused;
+};
+
+class tst_qv4debugger : public QObject
+{
+ Q_OBJECT
+private slots:
+ void init();
+ void cleanup();
+
+ void breakAnywhere();
+ void pendingBreakpoint();
+ void liveBreakPoint();
+ void removePendingBreakPoint();
+ void addBreakPointWhilePaused();
+ void removeBreakPointForNextInstruction();
+
+private:
+ void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1)
+ {
+ QMetaObject::invokeMethod(m_engine, "evaluate", Qt::QueuedConnection,
+ Q_ARG(QString, script), Q_ARG(QString, fileName),
+ Q_ARG(int, lineNumber));
+ waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0);
+ }
+
+ TestEngine *m_engine;
+ QV4::ExecutionEngine *m_v4;
+ TestAgent *m_debuggerAgent;
+ QThread *m_javaScriptThread;
+};
+
+void tst_qv4debugger::init()
+{
+ m_javaScriptThread = new QThread;
+ m_engine = new TestEngine;
+ m_v4 = m_engine->v4Engine();
+ m_v4->enableDebugger();
+ m_engine->moveToThread(m_javaScriptThread);
+ m_javaScriptThread->start();
+ m_debuggerAgent = new TestAgent;
+ m_debuggerAgent->addDebugger(m_v4->debugger);
+}
+
+void tst_qv4debugger::cleanup()
+{
+ m_javaScriptThread->exit();
+ waitForSignal(m_javaScriptThread, SIGNAL(finished()), /*timeout*/ 0);
+ delete m_engine;
+ delete m_javaScriptThread;
+ m_engine = 0;
+ m_v4 = 0;
+ QCOMPARE(m_debuggerAgent->debuggerCount(), 0);
+ delete m_debuggerAgent;
+ m_debuggerAgent = 0;
+}
+
+void tst_qv4debugger::breakAnywhere()
+{
+ QString script =
+ "var i = 42;\n"
+ "var j = i + 1\n"
+ "var k = i\n";
+ m_debuggerAgent->pause(m_v4->debugger);
+ evaluateJavaScript(script, "testFile");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+}
+
+void tst_qv4debugger::pendingBreakpoint()
+{
+ QString script =
+ "var i = 42;\n"
+ "var j = i + 1\n"
+ "var k = i\n";
+ m_debuggerAgent->addBreakPoint(m_v4->debugger, "testfile", 2);
+ evaluateJavaScript(script, "testfile");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1);
+ QV4::Debugging::Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first();
+ QCOMPARE(state.fileName, QString("testfile"));
+ QCOMPARE(state.lineNumber, 2);
+}
+
+void tst_qv4debugger::liveBreakPoint()
+{
+ QString script =
+ "var i = 42;\n"
+ "var j = i + 1\n"
+ "var k = i\n";
+ m_debuggerAgent->m_breakPointsToAddWhenPaused << TestAgent::TestBreakPoint("liveBreakPoint", 3);
+ m_debuggerAgent->pause(m_v4->debugger);
+ evaluateJavaScript(script, "liveBreakPoint");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 2);
+ QV4::Debugging::Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(1);
+ QCOMPARE(state.fileName, QString("liveBreakPoint"));
+ QCOMPARE(state.lineNumber, 3);
+}
+
+void tst_qv4debugger::removePendingBreakPoint()
+{
+ QString script =
+ "var i = 42;\n"
+ "var j = i + 1\n"
+ "var k = i\n";
+ m_debuggerAgent->addBreakPoint(m_v4->debugger, "removePendingBreakPoint", 2);
+ m_debuggerAgent->removeBreakPoint(m_v4->debugger, "removePendingBreakPoint", 2);
+ evaluateJavaScript(script, "removePendingBreakPoint");
+ QVERIFY(!m_debuggerAgent->m_wasPaused);
+}
+
+void tst_qv4debugger::addBreakPointWhilePaused()
+{
+ QString script =
+ "var i = 42;\n"
+ "var j = i + 1\n"
+ "var k = i\n";
+ m_debuggerAgent->addBreakPoint(m_v4->debugger, "addBreakPointWhilePaused", 1);
+ m_debuggerAgent->m_breakPointsToAddWhenPaused << TestAgent::TestBreakPoint("addBreakPointWhilePaused", 2);
+ evaluateJavaScript(script, "addBreakPointWhilePaused");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 2);
+
+ QV4::Debugging::Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(0);
+ QCOMPARE(state.fileName, QString("addBreakPointWhilePaused"));
+ QCOMPARE(state.lineNumber, 1);
+
+ state = m_debuggerAgent->m_statesWhenPaused.at(1);
+ QCOMPARE(state.fileName, QString("addBreakPointWhilePaused"));
+ QCOMPARE(state.lineNumber, 2);
+}
+
+static QV4::Value someCall(QV4::SimpleCallContext *ctx)
+{
+ ctx->engine->debugger->removeBreakPoint("removeBreakPointForNextInstruction", 2);
+ return QV4::Value::undefinedValue();
+}
+
+void tst_qv4debugger::removeBreakPointForNextInstruction()
+{
+ QString script =
+ "someCall();\n"
+ "var i = 42;";
+
+ QMetaObject::invokeMethod(m_engine, "injectFunction", Qt::BlockingQueuedConnection,
+ Q_ARG(QString, "someCall"), Q_ARG(TestEngine::InjectedFunction, someCall));
+
+ m_debuggerAgent->addBreakPoint(m_v4->debugger, "removeBreakPointForNextInstruction", 2);
+
+ evaluateJavaScript(script, "removeBreakPointForNextInstruction");
+ QVERIFY(!m_debuggerAgent->m_wasPaused);
+}
+
+QTEST_MAIN(tst_qv4debugger)
+
+#include "tst_qv4debugger.moc"
diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp
index cc1dc71b60..558055e677 100644
--- a/tools/v4/main.cpp
+++ b/tools/v4/main.cpp
@@ -43,7 +43,6 @@
# include "private/qv4_llvm_p.h"
#endif // QMLJS_WITH_LLVM
-#include "private/qv4debugging_p.h"
#include "private/qv4object_p.h"
#include "private/qv4runtime_p.h"
#include "private/qv4functionobject_p.h"
@@ -287,17 +286,9 @@ int main(int argc, char *argv[])
#ifdef QMLJS_WITH_LLVM
QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject;
#endif // QMLJS_WITH_LLVM
- bool enableDebugging = false;
bool runAsQml = false;
if (!args.isEmpty()) {
- if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) {
- enableDebugging = true;
- args.removeFirst();
- }
- }
-
- if (!args.isEmpty()) {
if (args.first() == QLatin1String("--jit")) {
mode = use_masm;
args.removeFirst();
@@ -375,11 +366,6 @@ int main(int argc, char *argv[])
QV4::ExecutionEngine vm(iSelFactory);
- QScopedPointer<QQmlJS::Debugging::Debugger> debugger;
- if (enableDebugging)
- debugger.reset(new QQmlJS::Debugging::Debugger(&vm));
- vm.debugger = debugger.data();
-
QV4::ExecutionContext *ctx = vm.rootContext;
QV4::Object *globalObject = vm.globalObject;