qmlengine.cpp 43.8 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 123
};

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

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
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
157 158 159
        if (!ast->statement)
            return true;

160 161 162 163 164 165 166 167 168 169 170
        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
171 172
            if (!block || !block->statements)
                return true;
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 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
            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;
};
291

292 293 294 295 296 297
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

    qtMessageLogHandler()->setHasEditableRow(true);
358 359 360 361 362

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
363 364 365
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
366
{
367 368 369 370 371 372
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

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

374 375 376 377 378 379 380 381 382 383
    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
384 385
    delete d;
}
386

hjk's avatar
hjk committed
387
void QmlEngine::setupInferior()
388
{
hjk's avatar
hjk committed
389
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
390

391
    notifyInferiorSetupOk();
hjk's avatar
hjk committed
392
}
hjk's avatar
hjk committed
393

con's avatar
con committed
394
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
395
{
396
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
397 398
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
399 400 401 402
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

403 404 405 406
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
407

408 409
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
410
}
411

412
void QmlEngine::beginConnection(quint16 port)
413 414
{
    d->m_noDebugOutputTimer.stop();
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    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
435 436
}

437
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
438
{
439 440 441 442 443 444 445 446
    if (isSlaveEngine()) {
        if (masterEngine()->state() != InferiorRunOk) {
            // we're right now debugging C++, just try longer ...
            beginConnection();
            return;
        }
    }

hjk's avatar
hjk committed
447
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
448 449
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
450 451 452 453 454
    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);
455 456 457
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
458
            this, SLOT(errorMessageBoxFinished(int)));
459 460 461 462

    infoBox->show();
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
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();
}

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

502
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
503
{
hjk's avatar
hjk committed
504
    if (socketError == QAbstractSocket::RemoteHostClosedError)
505
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
506

507 508 509 510
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
511 512
}

513 514
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
515 516
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
517 518
}

519 520 521 522 523
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

524
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
525
{
526
    d->m_outputParser.processOutput(output);
527 528 529 530 531 532 533 534 535 536
}

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

537 538 539
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
540 541
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
542 543 544 545 546
        // 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
547 548 549 550 551 552 553 554 555 556
        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) {
557 558 559 560 561 562
            editor = editorManager->openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

563 564
            updateEditor(editor, d->m_sourceDocuments.value(fileName));
        }
565
        editorManager->activateEditor(editor);
566

567 568 569 570 571
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

572 573
void QmlEngine::closeConnection()
{
574
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
575
    d->m_adapter.closeConnection();
576 577
}

hjk's avatar
hjk committed
578 579 580
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
581

582
    if (!isSlaveEngine()) {
583 584 585
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
586 587
            startApplicationLauncher();
    }
588 589 590 591 592
}

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

604 605 606 607 608 609
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
610 611
}

Christian Kandeler's avatar
Christian Kandeler committed
612
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
613
{
Christian Kandeler's avatar
Christian Kandeler committed
614
    Q_UNUSED(gdbServerPort);
615

Christian Kandeler's avatar
Christian Kandeler committed
616 617
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
618 619

    notifyEngineRemoteSetupDone();
620
    notifyEngineSetupOk();
621 622 623 624
}

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

    notifyEngineRemoteSetupFailed();
630
    notifyEngineSetupFailed();
631 632
}

hjk's avatar
hjk committed
633
void QmlEngine::shutdownInferior()
634
{
635 636 637 638
    d->m_noDebugOutputTimer.stop();

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

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

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

void QmlEngine::shutdownEngine()
{
651 652
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
653

654
    notifyEngineShutdownOk();
655 656
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
657 658
}

hjk's avatar
hjk committed
659
void QmlEngine::setupEngine()
660
{
661
    if (startParameters().requestRemoteSetup) {
662
        // we need to get the port first
663
        notifyEngineRequestRemoteSetup();
664 665 666
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
667

668 669 670 671 672 673 674
        // 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();
    }
675 676
}

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

void QmlEngine::interruptInferior()
{
690 691 692
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
693
    notifyInferiorStopOk();
694 695 696 697
}

void QmlEngine::executeStep()
{
698 699 700
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
701 702
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
703 704 705 706
}

void QmlEngine::executeStepI()
{
707 708 709
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
710 711
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
712 713 714 715
}

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

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

void QmlEngine::executeNextI()
{
734
    executeNext();
735 736
}

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

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

760
void QmlEngine::executeJumpToLine(const ContextData &data)
761
{
762
    Q_UNUSED(data)
763 764 765 766 767
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
768 769 770
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

771 772 773
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
774
    gotoLocation(stackHandler()->frames().value(index));
775 776 777 778 779 780 781
}

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

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

789 790 791 792 793 794 795 796 797 798 799 800 801 802
    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;
    }

803
    if (d->m_adapter.activeDebuggerClient()) {
804
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
805 806
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
807
            client->insertBreakpoint(id, line, column);
808 809 810 811 812 813 814
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829

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

830 831 832 833 834
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
835
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
836 837
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
838
            client->removeBreakpoint(id);
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
        }
    }

    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()) {
855
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
856 857
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
858
            client->changeBreakpoint(id);
859 860 861 862 863 864 865 866
        }
    }

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

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

hjk's avatar
hjk committed
874
    BreakHandler *handler = breakHandler();
875

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

883
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
        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;
904
        }
905
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
906 907
    }

908
    DebuggerEngine::attemptBreakpointSynchronization();
909

910
    if (d->m_adapter.activeDebuggerClient()) {
911
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
912 913
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
914
            client->synchronizeBreakpoints();
915
        }
916
    }
917 918
}

919
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
920
{
921
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
922 923 924 925 926
            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
927
    //This is because the older client does not support BreakpointOnQmlSignalHandler
928 929 930 931 932
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
933 934
}

935 936 937 938 939 940 941 942 943 944 945 946 947
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

948 949 950 951 952 953 954
void QmlEngine::reloadSourceFiles()
{
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->getSourceFiles();
    }
}

955 956 957 958 959 960 961 962 963 964 965
void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

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

966
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
967
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
968
{
969
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
970
    // the qml js editor. Makes life easier.
971
    emit tooltipRequested(mousePos, editor, ctx.position);
972
    return true;
973 974 975 976 977 978 979 980
}

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

981
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
982
    const QString &expression, const QVariant &valueV)
983
{
984 985 986 987
    if (!expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(data,
                                                                   expression,
                                                                   valueV);
988
    }
989 990
}

hjk's avatar
hjk committed
991 992
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
993
{
Olivier Goffart's avatar
Olivier Goffart committed
994
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
995
    //watchHandler()->rebuildModel();
996
    showStatusMessage(tr("Stopped."), 5000);
997

998 999
    if (!data.name.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        if (data.isValueNeeded()) {
1000
            d->m_adapter.activeDebuggerClient()->updateWatchData(data);
1001 1002 1003 1004 1005
        }
        if (data.isChildrenNeeded()
                && watchHandler()->isExpandedIName(data.iname)) {
            d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
1006 1007 1008
    }

    synchronizeWatchers();
1009

Christiaan Janssen's avatar
Christiaan Janssen committed
1010 1011 1012 1013 1014 1015
    if (!data.isSomethingNeeded())
        watchHandler()->insertData(data);
}

void QmlEngine::synchronizeWatchers()
{
1016
    QStringList watchedExpressions = watchHandler()->watchedExpressions();
1017
    // send watchers list
1018 1019
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
1020
    } else {
1021 1022 1023
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients())
            client->synchronizeWatchers(watchedExpressions);
    }
1024 1025
}

1026 1027 1028