aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/v4
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 /src/qml/qml/v4
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>
Diffstat (limited to 'src/qml/qml/v4')
-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
11 files changed, 296 insertions, 275 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 {