qmlengine.cpp 43.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** 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.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29 30

#include "qmlengine.h"
31
#include "baseqmldebuggerclient.h"
32
#include "qmlinspectoragent.h"
33

Friedemann Kleint's avatar
Friedemann Kleint committed
34
#include "debuggerstartparameters.h"
35
#include "debuggeractions.h"
36
#include "debuggerconstants.h"
hjk's avatar
hjk committed
37
#include "debuggercore.h"
38
#include "debuggerdialogs.h"
39
#include "debuggerinternalconstants.h"
40
#include "debuggermainwindow.h"
hjk's avatar
hjk committed
41
#include "debuggerrunner.h"
42
#include "debuggerstringutils.h"
43
#include "debuggertooltipmanager.h"
44

45 46 47 48 49
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
50
#include "sourcefileshandler.h"
51 52
#include "watchutils.h"

Lasse Holmstedt's avatar
Lasse Holmstedt committed
53
#include <extensionsystem/pluginmanager.h>
54
#include <qmldebug/baseenginedebugclient.h>
55
#include <qmljseditor/qmljseditorconstants.h>
56 57
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
58

59
#include <utils/environment.h>
60 61
#include <utils/qtcassert.h>

62 63
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
64
#include <coreplugin/helpmanager.h>
65 66 67
#include <coreplugin/icore.h>

#include <texteditor/itexteditor.h>
68

69 70 71
#include <qmljstools/qmlconsolemanager.h>
#include <qmljstools/qmlconsoleitem.h>

72 73 74 75 76
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
77

78 79 80 81 82
#include <QAction>
#include <QApplication>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
83

84 85
#include <QTcpSocket>
#include <QHostAddress>
86

87 88 89 90 91 92 93 94
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

95 96
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
97

98 99 100
namespace Debugger {
namespace Internal {

Kai Koehne's avatar
Kai Koehne committed
101
class ASTWalker : public Visitor
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
public:
    void operator()(Node *ast, quint32 *l, quint32 *c)
    {
        done = false;
        line = l;
        column = c;
        Node::accept(ast, this);
    }

    bool preVisit(Node *ast)
    {
        return ast->lastSourceLocation().startLine >= *line && !done;
    }

    //Case 1: Breakpoint is between sourceStart(exclusive) and
    //        sourceEnd(inclusive) --> End tree walk.
    //Case 2: Breakpoint is on sourceStart --> Check for the start
    //        of the first executable code. Set the line number and
    //        column number. End tree walk.
    //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable"
    //        code and check for Case 2. End tree walk.

    //Add more types when suitable.

    bool visit(UiScriptBinding *ast)
    {
Aurindam Jana's avatar
Aurindam Jana committed
129 130 131
        if (!ast->statement)
            return true;

132 133 134 135 136 137 138 139 140 141 142
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 statementStartLine;
        quint32 statementColumn;

        if (ast->statement->kind == Node::Kind_ExpressionStatement) {
            statementStartLine = ast->statement->firstSourceLocation().
                    startLine;
            statementColumn = ast->statement->firstSourceLocation().startColumn;

        } else if (ast->statement->kind == Node::Kind_Block) {
            Block *block = static_cast<Block *>(ast->statement);
Aurindam Jana's avatar
Aurindam Jana committed
143 144
            if (!block || !block->statements)
                return true;
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
            statementStartLine = block->statements->firstSourceLocation().
                    startLine;
            statementColumn = block->statements->firstSourceLocation().
                    startColumn;

        } else {
            return true;
        }


        //Case 1
        //Check for possible relocation within the binding statement

        //Rewritten to (function <token>() { { }})
        //The offset 16 is position of inner lbrace without token length.
        const int offset = 16;

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(FunctionDeclaration *ast) {
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 sourceStartColumn = ast->firstSourceLocation().startColumn;
        quint32 statementStartLine = ast->body->firstSourceLocation().startLine;
        quint32 statementColumn = ast->body->firstSourceLocation().startColumn;

        //Case 1
        //Check for possible relocation within the function declaration

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(EmptyStatement *ast)
    {
        *line = ast->lastSourceLocation().startLine + 1;
        return true;
    }

    bool visit(VariableStatement *ast) { test(ast); return true; }
    bool visit(VariableDeclarationList *ast) { test(ast); return true; }
    bool visit(VariableDeclaration *ast) { test(ast); return true; }
    bool visit(ExpressionStatement *ast) { test(ast); return true; }
    bool visit(IfStatement *ast) { test(ast); return true; }
    bool visit(DoWhileStatement *ast) { test(ast); return true; }
    bool visit(WhileStatement *ast) { test(ast); return true; }
    bool visit(ForStatement *ast) { test(ast); return true; }
    bool visit(LocalForStatement *ast) { test(ast); return true; }
    bool visit(ForEachStatement *ast) { test(ast); return true; }
    bool visit(LocalForEachStatement *ast) { test(ast); return true; }
    bool visit(ContinueStatement *ast) { test(ast); return true; }
    bool visit(BreakStatement *ast) { test(ast); return true; }
    bool visit(ReturnStatement *ast) { test(ast); return true; }
    bool visit(WithStatement *ast) { test(ast); return true; }
    bool visit(SwitchStatement *ast) { test(ast); return true; }
    bool visit(CaseBlock *ast) { test(ast); return true; }
    bool visit(CaseClauses *ast) { test(ast); return true; }
    bool visit(CaseClause *ast) { test(ast); return true; }
    bool visit(DefaultClause *ast) { test(ast); return true; }
    bool visit(LabelledStatement *ast) { test(ast); return true; }
    bool visit(ThrowStatement *ast) { test(ast); return true; }
    bool visit(TryStatement *ast) { test(ast); return true; }
    bool visit(Catch *ast) { test(ast); return true; }
    bool visit(Finally *ast) { test(ast); return true; }
    bool visit(FunctionExpression *ast) { test(ast); return true; }
    bool visit(DebuggerStatement *ast) { test(ast); return true; }

    void test(Node *ast)
    {
        quint32 statementStartLine = ast->firstSourceLocation().startLine;
        //Case 1/2
        if (statementStartLine <= *line &&
                *line <= ast->lastSourceLocation().startLine)
            done = true;

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            *column = ast->firstSourceLocation().startColumn;
            done = true;
        }
    }

    bool done;
    quint32 *line;
    quint32 *column;
};
263

264 265 266 267 268 269
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

270 271
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
Kai Koehne's avatar
Kai Koehne committed
272 273 274 275
  , m_adapter(this)
  , m_inspectorAdapter(&m_adapter, this)
  , m_retryOnConnectFail(false)
  , m_automaticConnect(false)
276
{
Friedemann Kleint's avatar
Friedemann Kleint committed
277
    setObjectName(QLatin1String("QmlEngine"));
278

279
    ExtensionSystem::PluginManager::addObject(this);
280

Kai Koehne's avatar
Kai Koehne committed
281
    connect(&m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
282
        SLOT(connectionError(QAbstractSocket::SocketError)));
Kai Koehne's avatar
Kai Koehne committed
283
    connect(&m_adapter, SIGNAL(serviceConnectionError(QString)),
284
        SLOT(serviceConnectionError(QString)));
Kai Koehne's avatar
Kai Koehne committed
285
    connect(&m_adapter, SIGNAL(connected()),
286
        SLOT(connectionEstablished()));
Kai Koehne's avatar
Kai Koehne committed
287
    connect(&m_adapter, SIGNAL(connectionStartupFailed()),
288 289
        SLOT(connectionStartupFailed()));

290 291 292 293
    connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)),
            SLOT(updateCurrentContext()));
    connect(this->stackHandler(), SIGNAL(currentIndexChanged()),
            SLOT(updateCurrentContext()));
Kai Koehne's avatar
Kai Koehne committed
294
    connect(&m_inspectorAdapter, SIGNAL(selectionChanged()),
295
            SLOT(updateCurrentContext()));
Kai Koehne's avatar
Kai Koehne committed
296
    connect(m_inspectorAdapter.agent(), SIGNAL(
297 298
                expressionResult(quint32,QVariant)),
            SLOT(expressionEvaluated(quint32,QVariant)));
Kai Koehne's avatar
Kai Koehne committed
299
    connect(m_adapter.messageClient(),
300
            SIGNAL(message(QtMsgType,QString,
301
                           QmlDebug::QDebugContextInfo)),
302
            SLOT(appendDebugOutput(QtMsgType,QString,
303
                                   QmlDebug::QDebugContextInfo)));
304

305

Kai Koehne's avatar
Kai Koehne committed
306
    connect(&m_applicationLauncher,
307 308
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
Kai Koehne's avatar
Kai Koehne committed
309
    connect(&m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
310 311
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
Kai Koehne's avatar
Kai Koehne committed
312
    connect(&m_applicationLauncher,
313
            SIGNAL(processStarted()),
Kai Koehne's avatar
Kai Koehne committed
314
            &m_noDebugOutputTimer,
315
            SLOT(start()));
316

Kai Koehne's avatar
Kai Koehne committed
317 318 319
    m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher
                                   ::msgWinCannotRetrieveDebuggingOutput());
    connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
320
            this, SLOT(beginConnection(quint16)));
Kai Koehne's avatar
Kai Koehne committed
321
    connect(&m_outputParser, SIGNAL(waitingForConnectionViaOst()),
322
            this, SLOT(beginConnection()));
Kai Koehne's avatar
Kai Koehne committed
323
    connect(&m_outputParser, SIGNAL(noOutputMessage()),
324
            this, SLOT(tryToConnect()));
Kai Koehne's avatar
Kai Koehne committed
325
    connect(&m_outputParser, SIGNAL(errorMessage(QString)),
326
            this, SLOT(appStartupFailed(QString)));
327

Kai Koehne's avatar
Kai Koehne committed
328 329 330 331 332
    // Only wait 8 seconds for the 'Waiting for connection' on application output,
    // then just try to connect (application output might be redirected / blocked)
    m_noDebugOutputTimer.setSingleShot(true);
    m_noDebugOutputTimer.setInterval(8000);
    connect(&m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
333

334 335 336 337
    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
338 339

    // we won't get any debug output
340
    if (startParameters.useTerminal) {
Kai Koehne's avatar
Kai Koehne committed
341 342 343
        m_noDebugOutputTimer.setInterval(0);
        m_retryOnConnectFail = true;
        m_automaticConnect = true;
344
    }
345 346 347
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
348
{
349 350
    if (ExtensionSystem::PluginManager::allObjects().contains(this)) {
        ExtensionSystem::PluginManager::removeObject(this);
351
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
352

353 354 355
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
Kai Koehne's avatar
Kai Koehne committed
356
    for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) {
357 358 359 360 361
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);
hjk's avatar
hjk committed
362
}
363

hjk's avatar
hjk committed
364
void QmlEngine::setupInferior()
365
{
hjk's avatar
hjk committed
366
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
367

368
    notifyInferiorSetupOk();
369

Kai Koehne's avatar
Kai Koehne committed
370
    if (m_automaticConnect)
371
        beginConnection();
hjk's avatar
hjk committed
372
}
hjk's avatar
hjk committed
373

con's avatar
con committed
374
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
375
{
376
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
377 378
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
379 380 381 382
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

383 384 385 386
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
387

388 389
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
390
}
391

392 393
void QmlEngine::tryToConnect(quint16 port)
{
394
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
Kai Koehne's avatar
Kai Koehne committed
395
    m_retryOnConnectFail = true;
396 397 398 399 400 401 402 403 404 405 406
    if (state() == EngineRunRequested) {
        if (isSlaveEngine()) {
            // Probably cpp is being debugged and hence we did not get the output yet.
            if (!masterEngine()->isDying())
                m_noDebugOutputTimer.start();
            else
                appStartupFailed(tr("No application output received in time"));
        } else {
            beginConnection(port);
        }
    } else {
Kai Koehne's avatar
Kai Koehne committed
407
        m_automaticConnect = true;
408
    }
409 410
}

411
void QmlEngine::beginConnection(quint16 port)
412
{
Kai Koehne's avatar
Kai Koehne committed
413
    m_noDebugOutputTimer.stop();
414

Kai Koehne's avatar
Kai Koehne committed
415
    if (state() != EngineRunRequested && m_retryOnConnectFail)
416 417
        return;

418
    QTC_ASSERT(state() == EngineRunRequested, return);
419

420 421 422 423
    if (port > 0) {
        QTC_ASSERT(startParameters().connParams.port == 0
                   || startParameters().connParams.port == port,
                   qWarning() << "Port " << port << "from application output does not match"
424
                   << startParameters().connParams.port << "from start parameters.");
Kai Koehne's avatar
Kai Koehne committed
425
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
426 427
        return;
    }
428 429 430
    // no port from application output, use the one from start parameters ...
    m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
                                 startParameters().qmlServerPort);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
431 432
}

433
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
434
{
Kai Koehne's avatar
Kai Koehne committed
435
    if (m_retryOnConnectFail) {
436 437
        // retry after 3 seconds ...
        QTimer::singleShot(3000, this, SLOT(beginConnection()));
438 439
        return;
    }
440

hjk's avatar
hjk committed
441
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
442 443
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
444 445 446 447 448
    infoBox->setText(tr("Could not connect to the in-process QML debugger."
                        "\nDo you want to retry?"));
    infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel |
                                QMessageBox::Help);
    infoBox->setDefaultButton(QMessageBox::Retry);
449 450 451
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
452
            this, SLOT(errorMessageBoxFinished(int)));
453 454 455 456

    infoBox->show();
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
void QmlEngine::appStartupFailed(const QString &errorMessage)
{
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
    infoBox->setText(tr("Could not connect to the in-process QML debugger."
                        "\n%1").arg(errorMessage));
    infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
    infoBox->setDefaultButton(QMessageBox::Ok);
    connect(infoBox, SIGNAL(finished(int)),
            this, SLOT(errorMessageBoxFinished(int)));
    infoBox->show();

    notifyEngineRunFailed();
}

473
void QmlEngine::errorMessageBoxFinished(int result)
474 475
{
    switch (result) {
476
    case QMessageBox::Retry: {
477
        beginConnection();
478 479 480 481
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
482
        helpManager->handleHelpRequest(QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
483
        // fall through
484 485
    }
    default:
486 487 488
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
489
        } else if (state() == EngineRunRequested) {
490
            notifyEngineRunFailed();
491
        }
Kai Koehne's avatar
Kai Koehne committed
492
        break;
493
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
494 495
}

496
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
497
{
hjk's avatar
hjk committed
498
    if (socketError == QAbstractSocket::RemoteHostClosedError)
499
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
500

501 502 503 504
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
505 506
}

507 508
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
509 510
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
511 512
}

513 514 515 516 517
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

518
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
519
{
Kai Koehne's avatar
Kai Koehne committed
520
    m_outputParser.processOutput(output);
521 522 523 524 525 526 527 528 529 530
}

void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const
{
    if (channel == AppOutput || channel == AppError) {
        const_cast<QmlEngine*>(this)->filterApplicationMessage(msg, channel);
    }
    DebuggerEngine::showMessage(msg, channel, timeout);
}

531 532 533
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
534 535
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
536
        // internal file from source files -> show generated .js
Kai Koehne's avatar
Kai Koehne committed
537
        QTC_ASSERT(m_sourceDocuments.contains(fileName), return);
538 539 540
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
541 542 543 544 545 546 547 548 549 550
        QString titlePattern = tr("JS Source for %1").arg(fileName);
        //Check if there are open editors with the same title
        QList<Core::IEditor *> editors = editorManager->openedEditors();
        foreach (Core::IEditor *ed, editors) {
            if (ed->displayName() == titlePattern) {
                editor = ed;
                break;
            }
        }
        if (!editor) {
hjk's avatar
hjk committed
551
            editor = Core::EditorManager::openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
552 553 554 555 556
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

Kai Koehne's avatar
Kai Koehne committed
557
            updateEditor(editor, m_sourceDocuments.value(fileName));
558
        }
hjk's avatar
hjk committed
559
        Core::EditorManager::activateEditor(editor);
560

561 562 563 564 565
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

566 567
void QmlEngine::closeConnection()
{
568
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Kai Koehne's avatar
Kai Koehne committed
569
    m_adapter.closeConnection();
570 571
}

hjk's avatar
hjk committed
572 573 574
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
575

576
    if (!isSlaveEngine()) {
577
        if (startParameters().startMode == AttachToRemoteServer)
578 579
            m_noDebugOutputTimer.start();
        else if (startParameters().startMode == AttachToRemoteProcess)
580 581
            beginConnection();
        else
582
            startApplicationLauncher();
583
    } else {
Kai Koehne's avatar
Kai Koehne committed
584
        m_noDebugOutputTimer.start();
585
    }
586 587 588 589
}

void QmlEngine::startApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
590
    if (!m_applicationLauncher.isRunning()) {
591 592 593 594
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
595
                     , Utils::NormalMessageFormat);
Kai Koehne's avatar
Kai Koehne committed
596
        m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui,
597 598 599
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
600
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
601

602 603
void QmlEngine::stopApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
604 605 606 607
    if (m_applicationLauncher.isRunning()) {
        disconnect(&m_applicationLauncher, SIGNAL(processExited(int)),
                   this, SLOT(disconnected()));
        m_applicationLauncher.stop();
608
    }
609 610
}

611
void QmlEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
612
{
Christian Kandeler's avatar
Christian Kandeler committed
613 614
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
615

616
    DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
617
    notifyEngineSetupOk();
618 619 620 621 622 623

    // The remote setup can take while especialy with mixed debugging.
    // Just waiting for 8 seconds is not enough. Increase the timeout
    // to 60 s
    // In case we get an output the m_outputParser will start the connection.
    m_noDebugOutputTimer.setInterval(60000);
624 625
}

626
void QmlEngine::notifyEngineRemoteSetupFailed(const QString &message)
627
{
628
    DebuggerEngine::notifyEngineRemoteSetupFailed(message);
629 630 631
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
632

633
    notifyEngineSetupFailed();
634 635
}

hjk's avatar
hjk committed
636
void QmlEngine::shutdownInferior()
637
{
Kai Koehne's avatar
Kai Koehne committed
638 639
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->endSession();
640

641 642
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
643
    }
644
    stopApplicationLauncher();
645
    closeConnection();
646

hjk's avatar
hjk committed
647 648 649 650 651
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
652 653 654
    m_noDebugOutputTimer.stop();

   // double check (ill engine?):
655
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
656

657
    notifyEngineShutdownOk();
658 659
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
660 661
}

hjk's avatar
hjk committed
662
void QmlEngine::setupEngine()
663
{
664
    if (startParameters().remoteSetupNeeded) {
665
        // we need to get the port first
666
        notifyEngineRequestRemoteSetup();
667
    } else {
Kai Koehne's avatar
Kai Koehne committed
668 669
        m_applicationLauncher.setEnvironment(startParameters().environment);
        m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
670

671
        // We can't do this in the constructore because runControl() isn't yet defined
Kai Koehne's avatar
Kai Koehne committed
672
        connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
673 674 675 676 677
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
678 679
}

680 681
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
682
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Kai Koehne's avatar
Kai Koehne committed
683 684
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->continueInferior();
685
    }
686
    resetLocation();
hjk's avatar
hjk committed
687 688
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
689 690 691 692
}

void QmlEngine::interruptInferior()
{
Kai Koehne's avatar
Kai Koehne committed
693 694
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->interruptInferior();
695
    }
696
    notifyInferiorStopOk();
697 698 699 700
}

void QmlEngine::executeStep()
{
Kai Koehne's avatar
Kai Koehne committed
701 702
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStep();
703
    }
hjk's avatar
hjk committed
704 705
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
706 707 708 709
}

void QmlEngine::executeStepI()
{
Kai Koehne's avatar
Kai Koehne committed
710 711
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStepI();
712
    }
hjk's avatar
hjk committed
713 714
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
715 716 717 718
}

void QmlEngine::executeStepOut()
{
Kai Koehne's avatar
Kai Koehne committed
719 720
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStepOut();
721
    }
hjk's avatar
hjk committed
722 723
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
724 725 726 727
}

void QmlEngine::executeNext()
{
Kai Koehne's avatar
Kai Koehne committed
728 729
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeNext();
730
    }
hjk's avatar
hjk committed
731 732
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
733 734 735 736
}

void QmlEngine::executeNextI()
{
737
    executeNext();
738 739
}

740
void QmlEngine::executeRunToLine(const ContextData &data)
741
{
742
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Jarek Kobus's avatar
Jarek Kobus committed
743
    showStatusMessage(tr("Run to line %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
744
    resetLocation();
745 746 747 748 749 750
    ContextData modifiedData = data;
    quint32 line = data.lineNumber;
    quint32 column;
    bool valid;
    if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
        modifiedData.lineNumber = line;
Kai Koehne's avatar
Kai Koehne committed
751 752
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
753 754
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
755 756 757 758 759 760 761 762
}

void QmlEngine::executeRunToFunction(const QString &functionName)
{
    Q_UNUSED(functionName)
    XSDEBUG("FIXME:  QmlEngine::executeRunToFunction()");
}

763
void QmlEngine::executeJumpToLine(const ContextData &data)
764
{
765
    Q_UNUSED(data)
766 767 768 769 770
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
771 772 773
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

Kai Koehne's avatar
Kai Koehne committed
774 775
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->activateFrame(index);
776
    }
777
    gotoLocation(stackHandler()->frames().value(index));
778 779 780 781 782 783 784
}

void QmlEngine::selectThread(int index)
{
    Q_UNUSED(index)
}

785 786 787 788 789 790 791
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

792 793 794 795 796 797 798
    const BreakpointParameters &params = handler->breakpointData(id);
    quint32 line = params.lineNumber;
    quint32 column = 0;
    if (params.type == BreakpointByFileAndLine) {
        bool valid = false;
        if (!adjustBreakpointLineAndColumn(params.fileName, &line, &column,
                                           &valid)) {
Kai Koehne's avatar
Kai Koehne committed
799
            pendingBreakpoints.insertMulti(params.fileName, id);
800 801 802 803 804 805
            return;
        }
        if (!valid)
            return;
    }

Kai Koehne's avatar
Kai Koehne committed
806 807
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
808
    } else {
Kai Koehne's avatar
Kai Koehne committed
809
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
810
            client->insertBreakpoint(id, line, column);
811 812 813 814 815 816 817
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
818 819 820

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
Kai Koehne's avatar
Kai Koehne committed
821
            pendingBreakpoints.contains(params.fileName)) {
822
        QHash<QString, BreakpointModelId>::iterator i =
Kai Koehne's avatar
Kai Koehne committed
823 824
                pendingBreakpoints.find(params.fileName);
        while (i != pendingBreakpoints.end() && i.key() == params.fileName) {
825
            if (i.value() == id) {
Kai Koehne's avatar
Kai Koehne committed
826
                pendingBreakpoints.erase(i);
827 828 829 830 831 832
                return;
            }
            ++i;
        }
    }

833 834 835 836
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

Kai Koehne's avatar
Kai Koehne committed
837 838
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->removeBreakpoint(id);
839
    } else {
Kai Koehne's avatar
Kai Koehne committed
840
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
841
            client->removeBreakpoint(id);
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
        }
    }

    if (handler->state(id) == BreakpointRemoveProceeding) {
        handler->notifyBreakpointRemoveOk(id);
    }
}

void QmlEngine::changeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << id << this << state);
    handler->notifyBreakpointChangeProceeding(id);

Kai Koehne's avatar
Kai Koehne committed
857 858
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->changeBreakpoint(id);
859
    } else {
Kai Koehne's avatar
Kai Koehne committed
860
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
861
            client->changeBreakpoint(id);
862 863 864 865 866 867 868 869
        }
    }

    if (handler->state(id) == BreakpointChangeProceeding) {
        handler->notifyBreakpointChangeOk(id);
    }
}

870 871
void QmlEngine::attemptBreakpointSynchronization()
{
872 873 874 875 876
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
877
    BreakHandler *handler = breakHandler();
878

879
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
880
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
881 882
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
883
            handler->setEngine(id, bpOwner);
884 885
    }

886
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
        switch (handler->state(id)) {
        case BreakpointNew:
            // Should not happen once claimed.
            QTC_CHECK(false);
            continue;
        case BreakpointInsertRequested:
            insertBreakpoint(id);
            continue;
        case BreakpointChangeRequested:
            changeBreakpoint(id);
            continue;
        case BreakpointRemoveRequested:
            removeBreakpoint(id);
            continue;
        case BreakpointChangeProceeding:
        case BreakpointInsertProceeding:
        case BreakpointRemoveProceeding:
        case BreakpointInserted:
        case BreakpointDead:
            continue;
907
        }
908
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
909 910
    }

911
    DebuggerEngine::attemptBreakpointSynchronization();
912

Kai Koehne's avatar
Kai Koehne committed
913 914
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
915
    } else {
Kai Koehne's avatar
Kai Koehne committed
916
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
917
            client->synchronizeBreakpoints();
918
        }
919
    }
920 921
}

hjk's avatar