qmlengine.cpp 44.2 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30 31 32 33
**
**************************************************************************/

#include "qmlengine.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
34
#include "qmladapter.h"
35
#include "interactiveinterpreter.h"
36

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

48 49 50 51 52
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
53
#include "sourcefileshandler.h"
54
#include "watchutils.h"
55
#include "qtmessageloghandler.h"
56

Lasse Holmstedt's avatar
Lasse Holmstedt committed
57
#include <extensionsystem/pluginmanager.h>
58
#include <projectexplorer/applicationlauncher.h>
59
#include <qmljsdebugclient/qdeclarativeoutputparser.h>
60
#include <qmljseditor/qmljseditorconstants.h>
61 62
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
63

64
#include <utils/environment.h>
65 66
#include <utils/qtcassert.h>

67 68
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
69
#include <coreplugin/helpmanager.h>
70 71 72
#include <coreplugin/icore.h>

#include <texteditor/itexteditor.h>
73

74 75 76 77 78
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
79

80 81 82 83 84 85 86
#include <QAction>
#include <QApplication>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
#include <QTextDocument>
87

88 89
#include <QTcpSocket>
#include <QHostAddress>
90

91 92 93 94 95 96 97 98
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

hjk's avatar
hjk committed
99
using namespace ProjectExplorer;
100 101
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
102

103 104 105
namespace Debugger {
namespace Internal {

106 107
class QmlEnginePrivate
{
108
public:
109 110
    explicit QmlEnginePrivate(QmlEngine *q);

111
private:
112
    friend class QmlEngine;
hjk's avatar
hjk committed
113 114
    QmlAdapter m_adapter;
    ApplicationLauncher m_applicationLauncher;
115
    QTimer m_noDebugOutputTimer;
116
    QmlJsDebugClient::QDeclarativeOutputParser m_outputParser;
117 118
    QHash<QString, QTextDocument*> m_sourceDocuments;
    QHash<QString, QWeakPointer<TextEditor::ITextEditor> > m_sourceEditors;
119 120
    InteractiveInterpreter m_interpreter;
    bool m_validContext;
121
    QHash<QString,BreakpointModelId> pendingBreakpoints;
122
    bool m_retryOnConnectFail;
123 124
};

125
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
126
    : m_adapter(q),
127 128
      m_validContext(false),
      m_retryOnConnectFail(false)
129 130
{}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
class ASTWalker: public Visitor
{
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
159 160 161
        if (!ast->statement)
            return true;

162 163 164 165 166 167 168 169 170 171 172
        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
173 174
            if (!block || !block->statements)
                return true;
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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
            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;
};
293

294 295 296 297 298 299
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

300 301
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
302
  : DebuggerEngine(startParameters, QmlLanguage, masterEngine),
303
    d(new QmlEnginePrivate(this))
304
{
Friedemann Kleint's avatar
Friedemann Kleint committed
305
    setObjectName(QLatin1String("QmlEngine"));
306 307 308 309 310 311 312 313 314 315 316 317 318 319

    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();
    pluginManager->addObject(this);

    connect(&d->m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
        SLOT(connectionError(QAbstractSocket::SocketError)));
    connect(&d->m_adapter, SIGNAL(serviceConnectionError(QString)),
        SLOT(serviceConnectionError(QString)));
    connect(&d->m_adapter, SIGNAL(connected()),
        SLOT(connectionEstablished()));
    connect(&d->m_adapter, SIGNAL(connectionStartupFailed()),
        SLOT(connectionStartupFailed()));

320 321 322 323 324 325
    connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)),
            SLOT(updateCurrentContext()));
    connect(this->stackHandler(), SIGNAL(currentIndexChanged()),
            SLOT(updateCurrentContext()));
    connect(&d->m_adapter, SIGNAL(selectionChanged()),
            SLOT(updateCurrentContext()));
326 327 328 329 330
    connect(d->m_adapter.messageClient(),
            SIGNAL(message(QtMsgType,QString,
                           QmlJsDebugClient::QDebugContextInfo)),
            SLOT(appendDebugOutput(QtMsgType,QString,
                                   QmlJsDebugClient::QDebugContextInfo)));
331

332 333 334 335
    connect(&d->m_applicationLauncher,
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
    connect(&d->m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
336 337
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
338 339 340 341
    connect(&d->m_applicationLauncher,
            SIGNAL(processStarted()),
            &d->m_noDebugOutputTimer,
            SLOT(start()));
342

343
    d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
344 345 346
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
            this, SLOT(beginConnection(quint16)));
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionViaOst()),
347 348
            this, SLOT(beginConnection()));
    connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
349
            this, SLOT(tryToConnect()));
350
    connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
351
            this, SLOT(appStartupFailed(QString)));
352

353
    // Only wait 8 seconds for the 'Waiting for connection' on application ouput, then just try to connect
354 355 356
    // (application output might be redirected / blocked)
    d->m_noDebugOutputTimer.setSingleShot(true);
    d->m_noDebugOutputTimer.setInterval(8000);
357
    connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
358 359

    qtMessageLogHandler()->setHasEditableRow(true);
360 361 362 363 364

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
365 366 367

    // we won't get any debug output
    d->m_retryOnConnectFail = startParameters.useTerminal;
368 369 370
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
371
{
372 373 374 375 376 377
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

    if (pluginManager->allObjects().contains(this)) {
        pluginManager->removeObject(this);
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
378

379 380 381 382 383 384 385 386 387 388
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
    for (iter = d->m_sourceEditors.begin(); iter != d->m_sourceEditors.end(); ++iter) {
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);

hjk's avatar
hjk committed
389 390
    delete d;
}
391

hjk's avatar
hjk committed
392
void QmlEngine::setupInferior()
393
{
hjk's avatar
hjk committed
394
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
395

396
    notifyInferiorSetupOk();
hjk's avatar
hjk committed
397
}
hjk's avatar
hjk committed
398

con's avatar
con committed
399
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
400
{
401
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
402 403
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
404 405 406 407
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

408 409 410 411
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
412

413 414
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
415
}
416

417 418 419 420 421 422
void QmlEngine::tryToConnect(quint16 port)
{
    d->m_retryOnConnectFail = true;
    beginConnection(port);
}

423
void QmlEngine::beginConnection(quint16 port)
424 425
{
    d->m_noDebugOutputTimer.stop();
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    if (port > 0) {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelTcpIp);
        QTC_ASSERT(startParameters().connParams.port == 0
                   || startParameters().connParams.port == port,
                   qWarning() << "Port " << port << "from application output does not match"
                   << startParameters().connParams.port << "from start parameters.")
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
        return;
    }
    if (startParameters().communicationChannel
           == DebuggerStartParameters::CommunicationChannelTcpIp) {
        // no port from application output, use the one from start parameters ...
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
                                        startParameters().qmlServerPort);
    } else {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelUsb);
        d->m_adapter.beginConnectionOst(startParameters().remoteChannel);
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
446 447
}

448
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
449
{
450 451 452 453 454 455 456
    if (isSlaveEngine()) {
        if (masterEngine()->state() != InferiorRunOk) {
            // we're right now debugging C++, just try longer ...
            beginConnection();
            return;
        }
    }
457 458 459 460
    if (d->m_retryOnConnectFail) {
        beginConnection();
        return;
    }
461

hjk's avatar
hjk committed
462
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
463 464
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
465 466 467 468 469
    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);
470 471 472
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
473
            this, SLOT(errorMessageBoxFinished(int)));
474 475 476 477

    infoBox->show();
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
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();
}

494
void QmlEngine::errorMessageBoxFinished(int result)
495 496
{
    switch (result) {
497
    case QMessageBox::Retry: {
498
        beginConnection();
499 500 501 502
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
503
        helpManager->handleHelpRequest(QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
504
        // fall through
505 506
    }
    default:
507 508 509
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
510
        } else if (state() == EngineRunRequested) {
511
            notifyEngineRunFailed();
512
        }
Kai Koehne's avatar
Kai Koehne committed
513
        break;
514
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
515 516
}

517
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
518
{
hjk's avatar
hjk committed
519
    if (socketError == QAbstractSocket::RemoteHostClosedError)
520
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
521

522 523 524 525
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
526 527
}

528 529
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
530 531
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
532 533
}

534 535 536 537 538
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

539
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
540
{
541
    d->m_outputParser.processOutput(output);
542 543 544 545 546 547 548 549 550 551
}

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);
}

552 553 554
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
555 556
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
557 558 559 560 561
        // internal file from source files -> show generated .js
        QTC_ASSERT(d->m_sourceDocuments.contains(fileName), return);
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
562 563 564 565 566 567 568 569 570 571
        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) {
572 573 574 575 576 577
            editor = editorManager->openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

578 579
            updateEditor(editor, d->m_sourceDocuments.value(fileName));
        }
580
        editorManager->activateEditor(editor);
581

582 583 584 585 586
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

587 588
void QmlEngine::closeConnection()
{
589
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
590
    d->m_adapter.closeConnection();
591 592
}

hjk's avatar
hjk committed
593 594 595
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
596

597
    if (!isSlaveEngine()) {
598 599 600
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
601
            startApplicationLauncher();
602 603
    } else {
        d->m_noDebugOutputTimer.start();
604
    }
605 606 607 608 609
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
610 611 612 613
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
614
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
615
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
616 617 618
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
619
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
620

621 622 623 624 625 626
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
627 628
}

Christian Kandeler's avatar
Christian Kandeler committed
629
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
630
{
Christian Kandeler's avatar
Christian Kandeler committed
631
    Q_UNUSED(gdbServerPort);
632

Christian Kandeler's avatar
Christian Kandeler committed
633 634
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
635 636

    notifyEngineRemoteSetupDone();
637
    notifyEngineSetupOk();
638 639 640 641
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
642 643 644
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
645 646

    notifyEngineRemoteSetupFailed();
647
    notifyEngineSetupFailed();
648 649
}

hjk's avatar
hjk committed
650
void QmlEngine::shutdownInferior()
651
{
652 653 654 655
    d->m_noDebugOutputTimer.stop();

    if (d->m_adapter.activeDebuggerClient())
        d->m_adapter.activeDebuggerClient()->endSession();
656

657 658
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
659
    }
660
    stopApplicationLauncher();
661
    closeConnection();
662

hjk's avatar
hjk committed
663 664 665 666 667
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
668 669
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
670

671
    notifyEngineShutdownOk();
672 673
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
674 675
}

hjk's avatar
hjk committed
676
void QmlEngine::setupEngine()
677
{
678
    if (startParameters().requestRemoteSetup) {
679
        // we need to get the port first
680
        notifyEngineRequestRemoteSetup();
681 682 683
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
684

685 686 687 688 689 690 691
        // We can't do this in the constructore because runControl() isn't yet defined
        connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
692 693
}

694 695
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
696
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
697 698 699
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
700
    resetLocation();
hjk's avatar
hjk committed
701 702
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
703 704 705 706
}

void QmlEngine::interruptInferior()
{
707 708 709
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
710
    notifyInferiorStopOk();
711 712 713 714
}

void QmlEngine::executeStep()
{
715 716 717
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
718 719
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
720 721 722 723
}

void QmlEngine::executeStepI()
{
724 725 726
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
727 728
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
729 730 731 732
}

void QmlEngine::executeStepOut()
{
733 734 735
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepOut();
    }
hjk's avatar
hjk committed
736 737
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
738 739 740 741
}

void QmlEngine::executeNext()
{
742 743 744
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeNext();
    }
hjk's avatar
hjk committed
745 746
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
747 748 749 750
}

void QmlEngine::executeNextI()
{
751
    executeNext();
752 753
}

754
void QmlEngine::executeRunToLine(const ContextData &data)
755
{
756 757 758
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
759 760 761 762 763 764
    ContextData modifiedData = data;
    quint32 line = data.lineNumber;
    quint32 column;
    bool valid;
    if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
        modifiedData.lineNumber = line;
765
    if (d->m_adapter.activeDebuggerClient())
766
        d->m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
767 768
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
769 770 771 772 773 774 775 776
}

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

777
void QmlEngine::executeJumpToLine(const ContextData &data)
778
{
779
    Q_UNUSED(data)
780 781 782 783 784
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
785 786 787
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

788 789 790
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
791
    gotoLocation(stackHandler()->frames().value(index));
792 793 794 795 796 797 798
}

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

799 800 801 802 803 804 805
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

806 807 808 809 810 811 812 813 814 815 816 817 818 819
    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)) {
            d->pendingBreakpoints.insertMulti(params.fileName, id);
            return;
        }
        if (!valid)
            return;
    }

820
    if (d->m_adapter.activeDebuggerClient()) {
821
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
822 823
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
824
            client->insertBreakpoint(id, line, column);
825 826 827 828 829 830 831
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
            d->pendingBreakpoints.contains(params.fileName)) {
        QHash<QString, BreakpointModelId>::iterator i =
                d->pendingBreakpoints.find(params.fileName);
        while (i != d->pendingBreakpoints.end() && i.key() == params.fileName) {
            if (i.value() == id) {
                d->pendingBreakpoints.erase(i);
                return;
            }
            ++i;
        }
    }

847 848 849 850 851
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
852
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
853 854
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
855
            client->removeBreakpoint(id);
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
        }
    }

    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);

    if (d->m_adapter.activeDebuggerClient()) {
872
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
873 874
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
875
            client->changeBreakpoint(id);
876 877 878 879 880 881 882 883
        }
    }

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

884 885
void QmlEngine::attemptBreakpointSynchronization()
{
886 887 888 889 890
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
891
    BreakHandler *handler = breakHandler();
892

893
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
894
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
895 896
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
897
            handler->setEngine(id, bpOwner);
898 899
    }

900
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
        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;
921
        }
922
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
923 924
    }

925
    DebuggerEngine::attemptBreakpointSynchronization();
926

927
    if (d->m_adapter.activeDebuggerClient()) {
928
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
929 930
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
931
            client->synchronizeBreakpoints();
932
        }
933
    }
934 935
}

936
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
937
{
938
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
939 940 941 942 943
            return true;

    //If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
    //TODO: enable setting of breakpoints before start of debug session
    //For now, the event breakpoint can be set after the activeDebuggerClient is known
944
    //This is because the older client does not support BreakpointOnQmlSignalHandler
945 946 947 948 949
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
950 951
}

952 953 954 955 956 957 958 959 960 961 962 963 964
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

965 966 967 968 969 970 971
void QmlEngine::reloadSourceFiles()
{
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->getSourceFiles();
    }
}

972 973 974 975 976 977 978 979 980 981 982
void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

//////////////////////////////////////////////////////////////////////
//
// Tooltip specific stuff
//
//////////////////////////////////////////////////////////////////////

983
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
984
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
985
{
986
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
987
    // the qml js editor. Makes life easier.
988
    emit tooltipRequested(mousePos, editor, ctx.position);
989
    return true;
990 991 992 993 994 995 996 997
}

//////////////////////////////////////////////////////////////////////
//
// Watch specific stuff
//
//////////////////////////////////////////////////////////////////////

998
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
999
    const QString &expression, const QVariant &valueV)
1000
{
1001 1002 1003 1004
    if (!expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(data,
                                                                   expression,
                                                                   valueV);
1005
    }
1006 1007
}

hjk's avatar
hjk committed
1008 1009
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
1010
{
Olivier Goffart's avatar
Olivier Goffart committed
1011
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
1012
    //watchHandler()->rebuildModel();
1013
    showStatusMessage(tr("Stopped."), 5000);
1014

1015 1016
    if (!data.name.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        if (data.isValueNeeded()) {
1017
            d->m_adapter.activeDebuggerClient()->updateWatchData(data);
1018 1019 1020 1021 1022
        }
        if (data.isChildrenNeeded()
                && watchHandler()->isExpandedIName(data.iname)) {
            d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
        }