9#include <private/qqmlcontext_p.h>
10#include <private/qqmlengine_p.h>
11#include <private/qv4compileddata_p.h>
12#include <private/qv4function_p.h>
13#include <private/qv4scopedvalue_p.h>
14#include <private/qv4script_p.h>
15#include <private/qv4stackframe_p.h>
21QV4Debugger::BreakPoint::BreakPoint(
const QString &fileName,
int line)
22 : fileName(fileName), lineNumber(line)
25inline size_t qHash(
const QV4Debugger::BreakPoint &b, size_t seed = 0)
noexcept
27 return qHash(b.fileName, seed) ^ b.lineNumber;
30inline bool operator==(
const QV4Debugger::BreakPoint &a,
31 const QV4Debugger::BreakPoint &b)
33 return a.lineNumber == b.lineNumber && a.fileName == b.fileName;
40 , m_pauseRequested(
false)
41 , m_haveBreakPoints(
false)
42 , m_breakOnThrow(
false)
44 , m_gatherSources(
nullptr)
45 , m_runningJob(
nullptr)
48 static int debuggerId = qRegisterMetaType<
QV4Debugger*>();
51 Q_UNUSED(pauseReasonId);
53 this, &
QV4Debugger::runJobUnpaused, Qt::QueuedConnection);
73 QMutexLocker locker(&m_lock);
76 m_pauseRequested =
true;
81 QMutexLocker locker(&m_lock);
85 if (!m_returnedValue.isUndefined())
86 m_returnedValue.set(m_engine, QV4::Encode::undefined());
88 m_currentFrame = m_engine->currentStackFrame;
90 m_runningCondition.wakeAll();
100 QMutexLocker locker(&m_lock);
101 m_breakPoints.insert(BreakPoint(fileName.mid(fileName.lastIndexOf(
'/') + 1),
102 lineNumber), {fileName, condition});
103 m_haveBreakPoints =
true;
104 m_breakPointsChanged =
true;
109 QMutexLocker locker(&m_lock);
110 m_breakPoints.remove(BreakPoint(fileName.mid(fileName.lastIndexOf(
'/') + 1),
112 m_haveBreakPoints = !m_breakPoints.isEmpty();
113 m_breakPointsChanged =
true;
118 QMutexLocker locker(&m_lock);
120 m_breakOnThrow = onoff;
125 QMutexLocker locker(&m_lock);
126 m_pauseRequested =
false;
131 QMutexLocker locker(&m_lock);
132 return m_executableLines.size();
138 state.fileName = QUrl(getFunction()->sourceFile()).fileName();
139 state
.lineNumber = engine()->currentStackFrame->lineNumber();
147 return m_pauseRequested || m_haveBreakPoints || m_breakPointsChanged || m_gatherSources
153 return m_engine->stackTrace(frameLimit);
161 QMutexLocker locker(&m_lock);
163 if (m_gatherSources) {
165 delete m_gatherSources;
166 m_gatherSources =
nullptr;
169 if (m_breakPointsChanged) {
170 m_breakPointsChanged =
false;
171 pruneExecutableLines();
174 switch (m_stepping) {
176 if (m_currentFrame != m_engine->currentStackFrame)
187 if (m_pauseRequested) {
188 m_pauseRequested =
false;
190 }
else if (m_haveBreakPoints) {
191 if (QV4::Function *f = getFunction()) {
193 const int lineNumber = engine()->currentStackFrame->lineNumber();
194 if (reallyHitTheBreakPoint(f, f->sourceFile(), lineNumber))
204 QMutexLocker locker(&m_lock);
206 if (m_stepping == StepIn)
207 m_currentFrame = m_engine->currentStackFrame;
216 QMutexLocker locker(&m_lock);
218 if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) {
219 m_currentFrame = m_currentFrame->parentFrame();
221 m_returnedValue.set(m_engine, retVal);
233 QMutexLocker locker(&m_lock);
239 if (m_engine->currentStackFrame)
240 return m_engine->currentStackFrame->v4Function;
242 return m_engine->globalCode;
247 QMutexLocker locker(&m_lock);
250 m_jobIsRunning.wakeAll();
259 emit debuggerPaused(
this, reason);
262 m_runningCondition.wait(&m_lock);
265 m_jobIsRunning.wakeAll();
275
276
277
278
279
280
281
283 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
285 ExecutableLines result;
286 const QV4::CompiledData::Unit *data = unit ? unit->unitData() :
nullptr;
289 for (uint f = 0, fn = data->functionTableSize; f < fn; ++f) {
290 const QV4::CompiledData::Function *cf = data->functionAt(f);
291 const int start = cf->location.line();
292 result.functionStarts.append(start);
293 const QV4::CompiledData::CodeOffsetToLineAndStatement *table
294 = cf->lineAndStatementNumberTable();
295 for (uint i = 0, n = cf->nLineAndStatementNumbers; i < n; ++i) {
296 const int line = table[i].line;
298 result.lines.append({line, start});
301 std::sort(result.lines.begin(), result.lines.end());
302 result.lines.erase(
std::unique(result.lines.begin(), result.lines.end()),
304 std::sort(result.functionStarts.begin(), result.functionStarts.end());
308int QV4Debugger::firstLineAtOrAfter(
const ExecutableLines &executable,
int requestedLine)
312 const auto it =
std::lower_bound(executable.lines.cbegin(), executable.lines.cend(),
313 ExecutableLine{requestedLine, 0});
314 if (it == executable.lines.cend())
323 if (it->functionStart > requestedLine)
325 const QList<
int> &starts = executable.functionStarts;
326 const auto start =
std::upper_bound(starts.cbegin(), starts.cend(), requestedLine);
327 if (start != starts.cend() && *start < it->line)
337 m_executableLines.removeIf([
this](
const UnitLines &cached) {
338 const QString file = QUrl(cached.unit->fileName()).fileName();
339 for (
auto it = m_breakPoints.keyBegin(), end = m_breakPoints.keyEnd(); it != end; ++it) {
340 if (it->fileName == file)
348
349
350
351
352
357 constexpr qsizetype MaxCachedUnits = 8;
359 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit
360 = function->executableCompilationUnit()->baseCompilationUnit();
361 for (qsizetype i = 0, n = m_executableLines.size(); i < n; ++i) {
362 if (m_executableLines.at(i).unit == unit) {
364 m_executableLines.move(i, 0);
365 return m_executableLines.first().lines;
368 if (m_executableLines.size() == MaxCachedUnits)
369 m_executableLines.removeLast();
370 m_executableLines.prepend({unit, collectExecutableLines(unit)});
371 return m_executableLines.first().lines;
375
376
377
378
379
380
381
384 QMutexLocker locker(&m_lock);
385 QV4::Function *f = getFunction();
390 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit
391 = f->executableCompilationUnit()->baseCompilationUnit();
392 for (
const UnitLines &cached : std::as_const(m_executableLines)) {
393 if (cached.unit == unit)
394 return firstLineAtOrAfter(cached.lines, requestedLine);
396 return firstLineAtOrAfter(collectExecutableLines(unit), requestedLine);
399bool QV4Debugger::reallyHitTheBreakPoint(QV4::Function *function,
const QString &filename,
402 const QString file = QUrl(filename).fileName();
408 QStringList conditions;
409 for (
auto bp = m_breakPoints.constBegin(), end = m_breakPoints.constEnd(); bp != end; ++bp) {
410 if (bp.key().fileName != file || bp.key().lineNumber > linenr)
414 if (bp.key().lineNumber != linenr
415 && (!filename.endsWith(bp.value().target)
416 || firstLineAtOrAfter(executableLines(function), bp.key().lineNumber)
420 if (bp.value().condition.isEmpty())
422 conditions.append(bp.value().condition);
425 for (
const QString &condition : std::as_const(conditions)) {
426 Q_ASSERT(m_runningJob ==
nullptr);
427 EvalJob evilJob(m_engine, condition);
428 m_runningJob = &evilJob;
430 m_runningJob =
nullptr;
431 if (evilJob.resultAsBoolean())
439 QMutexLocker locker(&m_lock);
440 runInEngine_havingLock(job);
446 Q_ASSERT(m_runningJob ==
nullptr);
450 m_runningCondition.wakeAll();
453 m_jobIsRunning.wait(&m_lock);
454 m_runningJob =
nullptr;
459#include "moc_qv4debugger.cpp"
friend bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept
Returns true if lhs and rhs are equal, otherwise returns false.
qsizetype executableLinesCacheSize()
void maybeBreakAtInstruction() override
QV4::Function * getFunction() const
void enteringFunction() override
void runInEngine(QV4DebugJob *job)
int resolvedBreakpointLine(int requestedLine)
QV4DataCollector * collector()
void removeBreakPoint(const QString &fileName, int lineNumber)
QV4::ExecutionEngine * engine() const
void setBreakOnThrow(bool onoff)
bool pauseAtNextOpportunity() const override
void leavingFunction(const QV4::ReturnedValue &retVal) override
ExecutionState currentExecutionState() const
const QV4DataCollector * collector() const
QV4Debugger(QV4::ExecutionEngine *engine)
void aboutToThrow() override
void addBreakPoint(const QString &fileName, int lineNumber, const QString &condition=QString())
QList< QV4::StackFrame > stackTrace(int frameLimit=-1) const
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept