QML Debugging: Fix crash when stepping through try-catch block.
authorErik Verbruggen <erik.verbruggen@digia.com>
Fri, 5 Dec 2014 12:16:14 +0000 (13:16 +0100)
committerErik Verbruggen <erik.verbruggen@theqtcompany.com>
Tue, 9 Dec 2014 10:55:04 +0000 (11:55 +0100)
Also fix the stack-trace generation, otherwise the debugger engine would
report a breakpoint hit on the wrong line.

Task-number: QTBUG-42723
Change-Id: I1f655a5174b28a1c9c31c85bbe023fbce5ddbb96
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/jsruntime/qv4context_p.h
src/qml/jsruntime/qv4debugging.cpp
src/qml/jsruntime/qv4engine.cpp
src/qml/jsruntime/qv4functionobject_p.h
tests/auto/qml/qv4debugger/tst_qv4debugger.cpp

index 6d95f03..c0ca4ae 100644 (file)
@@ -55,7 +55,6 @@ struct Function;
 }
 
 struct CallContext;
-struct CallContext;
 struct CatchContext;
 struct WithContext;
 
@@ -149,6 +148,9 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
 
     inline CallContext *asCallContext();
     inline const CallContext *asCallContext() const;
+    inline const CatchContext *asCatchContext() const;
+
+    inline FunctionObject *getFunctionObject() const;
 
     static void markObjects(Managed *m, ExecutionEngine *e);
 };
@@ -225,6 +227,24 @@ inline const CallContext *ExecutionContext::asCallContext() const
     return d()->type >= Type_SimpleCallContext ? static_cast<const CallContext *>(this) : 0;
 }
 
+inline const CatchContext *ExecutionContext::asCatchContext() const
+{
+    return d()->type == Type_CatchContext ? static_cast<const CatchContext *>(this) : 0;
+}
+
+inline FunctionObject *ExecutionContext::getFunctionObject() const
+{
+    for (const ExecutionContext *it = this; it; it = it->d()->parent) {
+        if (const CallContext *callCtx = it->asCallContext())
+            return callCtx->d()->function;
+        else if (it->asCatchContext())
+            continue; // look in the parent context for a FunctionObject
+        else
+            break;
+    }
+
+    return 0;
+}
 
 inline void ExecutionEngine::pushContext(CallContext *context)
 {
index 6a8d364..a835d83 100644 (file)
@@ -509,7 +509,6 @@ void Debugger::maybeBreakAtInstruction()
         return;
 
     QMutexLocker locker(&m_lock);
-    int lineNumber = engine()->currentContext()->d()->lineNumber;
 
     if (m_gatherSources) {
         m_gatherSources->run();
@@ -533,8 +532,12 @@ void Debugger::maybeBreakAtInstruction()
     if (m_pauseRequested) { // Serve debugging requests from the agent
         m_pauseRequested = false;
         pauseAndWait(PauseRequest);
-    } else if (m_haveBreakPoints && reallyHitTheBreakPoint(getFunction()->sourceFile(), lineNumber)) {
-        pauseAndWait(BreakPoint);
+    } else if (m_haveBreakPoints) {
+        if (Function *f = getFunction()) {
+            const int lineNumber = engine()->currentContext()->d()->lineNumber;
+            if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber))
+                pauseAndWait(BreakPoint);
+        }
     }
 }
 
@@ -579,12 +582,10 @@ void Debugger::aboutToThrow()
 Function *Debugger::getFunction() const
 {
     ExecutionContext *context = m_engine->currentContext();
-    if (CallContext *callCtx = context->asCallContext())
-        return callCtx->d()->function->function();
-    else {
-        Q_ASSERT(context->d()->type == QV4::ExecutionContext::Type_GlobalContext);
+    if (const FunctionObject *function = context->getFunctionObject())
+        return function->function();
+    else
         return context->d()->engine->globalCode;
-    }
 }
 
 void Debugger::pauseAndWait(PauseReason reason)
index ea075f9..74b262e 100644 (file)
@@ -700,19 +700,18 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
 
     QV4::ExecutionContext *c = currentContext();
     while (c && frameLimit) {
-        CallContext *callCtx = c->asCallContext();
-        if (callCtx && callCtx->d()->function) {
+        if (FunctionObject *function = c->getFunctionObject()) {
             StackFrame frame;
-            if (callCtx->d()->function->function())
-                frame.source = callCtx->d()->function->function()->sourceFile();
-            name = callCtx->d()->function->name();
+            if (const Function *f = function->function())
+                frame.source = f->sourceFile();
+            name = function->name();
             frame.function = name->toQString();
             frame.line = -1;
             frame.column = -1;
 
-            if (callCtx->d()->function->function())
+            if (function->function())
                 // line numbers can be negative for places where you can't set a real breakpoint
-                frame.line = qAbs(callCtx->d()->lineNumber);
+                frame.line = qAbs(c->d()->lineNumber);
 
             stack.append(frame);
             --frameLimit;
@@ -727,7 +726,6 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
         frame.line = rootContext->d()->lineNumber;
         frame.column = -1;
 
-
         stack.append(frame);
     }
     return stack;
index c8edb76..adedb9f 100644 (file)
@@ -116,7 +116,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
 
 
     ExecutionContext *scope() { return d()->scope; }
-    Function *function() { return d()->function; }
+    Function *function() const { return d()->function; }
 
     ReturnedValue name();
     unsigned int formalParameterCount() { return function() ? function()->compiledFunction->nFormals : 0; }
index 63bfffa..fcbdcbc 100644 (file)
@@ -286,6 +286,7 @@ private slots:
 
     // exceptions:
     void pauseOnThrow();
+    void breakInCatch();
 
     void evaluateExpression();
 
@@ -612,6 +613,25 @@ void tst_qv4debugger::pauseOnThrow()
     QCOMPARE(m_debuggerAgent->m_thrownValue.toString(), QString("hard"));
 }
 
+void tst_qv4debugger::breakInCatch()
+{
+    QString script =
+            "try {\n"
+            "    throw 'catch...'\n"
+            "} catch (e) {\n"
+            "    console.log(e, 'me');\n"
+            "}\n";
+
+    m_debuggerAgent->addBreakPoint("breakInCatch", 4);
+    evaluateJavaScript(script, "breakInCatch");
+    QVERIFY(m_debuggerAgent->m_wasPaused);
+    QCOMPARE(m_debuggerAgent->m_pauseReason, BreakPoint);
+    QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1);
+    QV4::Debugging::Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first();
+    QCOMPARE(state.fileName, QString("breakInCatch"));
+    QCOMPARE(state.lineNumber, 4);
+}
+
 void tst_qv4debugger::evaluateExpression()
 {
     QString script =