qmlengine.cpp 43.6 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(connectionStartupFailed(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(const QString &errorMessage)
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 455 456 457 458 459 460 461 462
    if (qobject_cast<QmlAdapter *>(sender())) {
        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);
    }
    if (qobject_cast<QmlJsDebugClient::QDeclarativeOutputParser *>(sender())) {
        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);
    }
463 464 465
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
466
            this, SLOT(errorMessageBoxFinished(int)));
467 468 469 470

    infoBox->show();
}

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

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

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

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

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

516
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
517
{
518
    d->m_outputParser.processOutput(output);
519 520 521 522 523 524 525 526 527 528
}

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

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

555 556
            updateEditor(editor, d->m_sourceDocuments.value(fileName));
        }
557
        editorManager->activateEditor(editor);
558

559 560 561 562 563
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

564 565
void QmlEngine::closeConnection()
{
566
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
567
    d->m_adapter.closeConnection();
568 569
}

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

574
    if (!isSlaveEngine()) {
575 576 577
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
578 579
            startApplicationLauncher();
    }
580 581 582 583 584
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
585 586 587 588
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
589
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
590
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
591 592 593
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
594
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
595

596 597 598 599 600 601
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
602 603
}

Christian Kandeler's avatar
Christian Kandeler committed
604
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
605
{
Christian Kandeler's avatar
Christian Kandeler committed
606
    Q_UNUSED(gdbServerPort);
607

Christian Kandeler's avatar
Christian Kandeler committed
608 609
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
610 611

    notifyEngineRemoteSetupDone();
612
    notifyEngineSetupOk();
613 614 615 616
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
617 618 619
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
620 621

    notifyEngineRemoteSetupFailed();
622
    notifyEngineSetupFailed();
623 624
}

hjk's avatar
hjk committed
625
void QmlEngine::shutdownInferior()
626
{
627 628 629 630
    d->m_noDebugOutputTimer.stop();

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

632 633
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
634
    }
635
    stopApplicationLauncher();
636
    closeConnection();
637

hjk's avatar
hjk committed
638 639 640 641 642
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
643 644
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
645

646
    notifyEngineShutdownOk();
647 648
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
649 650
}

hjk's avatar
hjk committed
651
void QmlEngine::setupEngine()
652
{
653
    if (startParameters().requestRemoteSetup) {
654
        // we need to get the port first
655
        notifyEngineRequestRemoteSetup();
656 657 658
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
659

660 661 662 663 664 665 666
        // 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();
    }
667 668
}

669 670
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
671
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
672 673 674
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
675
    resetLocation();
hjk's avatar
hjk committed
676 677
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
678 679 680 681
}

void QmlEngine::interruptInferior()
{
682 683 684
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
685
    notifyInferiorStopOk();
686 687 688 689
}

void QmlEngine::executeStep()
{
690 691 692
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
693 694
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
695 696 697 698
}

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

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

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

void QmlEngine::executeNextI()
{
726
    executeNext();
727 728
}

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

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

752
void QmlEngine::executeJumpToLine(const ContextData &data)
753
{
754
    Q_UNUSED(data)
755 756 757 758 759
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
760 761 762
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

763 764 765
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
766
    gotoLocation(stackHandler()->frames().value(index));
767 768 769 770 771 772 773
}

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

774 775 776 777 778 779 780
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

781 782 783 784 785 786 787 788 789 790 791 792 793 794
    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;
    }

795
    if (d->m_adapter.activeDebuggerClient()) {
796
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
797 798
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
799
            client->insertBreakpoint(id, line, column);
800 801 802 803 804 805 806
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821

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

822 823 824 825 826
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
827
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
828 829
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
830
            client->removeBreakpoint(id);
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
        }
    }

    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()) {
847
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
848 849
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
850
            client->changeBreakpoint(id);
851 852 853 854 855 856 857 858
        }
    }

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

859 860
void QmlEngine::attemptBreakpointSynchronization()
{
861 862 863 864 865
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
866
    BreakHandler *handler = breakHandler();
867

868
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
869
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
870 871
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
872
            handler->setEngine(id, bpOwner);
873 874
    }

875
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
        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;
896
        }
897
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
898 899
    }

900
    DebuggerEngine::attemptBreakpointSynchronization();
901

902
    if (d->m_adapter.activeDebuggerClient()) {
903
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
904 905
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
906
            client->synchronizeBreakpoints();
907
        }
908
    }
909 910
}

911
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
912
{
913
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
914 915 916 917 918
            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
919
    //This is because the older client does not support BreakpointOnQmlSignalHandler
920 921 922 923 924
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
925 926
}

927 928 929 930 931 932 933 934 935 936 937 938 939
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

940 941 942 943 944 945 946
void QmlEngine::reloadSourceFiles()
{
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->getSourceFiles();
    }
}

947 948 949 950 951 952 953 954 955 956 957
void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

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

958
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
959
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
960
{
961
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
962
    // the qml js editor. Makes life easier.
963
    emit tooltipRequested(mousePos, editor, ctx.position);
964
    return true;
965 966 967 968 969 970 971 972
}

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

973
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
974
    const QString &expression, const QVariant &valueV)
975
{
976 977 978 979
    if (!expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(data,
                                                                   expression,
                                                                   valueV);
980
    }
981 982
}

hjk's avatar
hjk committed
983 984
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
985
{
Olivier Goffart's avatar
Olivier Goffart committed
986
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
987
    //watchHandler()->rebuildModel();
988
    showStatusMessage(tr("Stopped."), 5000);
989

990 991
    if (!data.name.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        if (data.isValueNeeded()) {
992
            d->m_adapter.activeDebuggerClient()->updateWatchData(data);
993 994 995 996 997
        }
        if (data.isChildrenNeeded()
                && watchHandler()->isExpandedIName(data.iname)) {
            d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
998 999 1000
    }

    synchronizeWatchers();
1001

Christiaan Janssen's avatar
Christiaan Janssen committed
1002 1003 1004 1005 1006 1007
    if (!data.isSomethingNeeded())
        watchHandler()->insertData(data);
}

void QmlEngine::synchronizeWatchers()
{
1008
    QStringList watchedExpressions = watchHandler()->watchedExpressions();
1009
    // send watchers list
1010 1011
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
1012
    } else {
1013 1014 1015
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients())
            client->synchronizeWatchers(watchedExpressions);
    }
1016 1017
}

1018 1019 1020 1021 1022 1023
void QmlEngine::onDebugQueryStateChanged(
        QmlJsDebugClient::QDeclarativeDebugQuery::State state)
{
    QmlJsDebugClient::QDeclarativeDebugExpressionQuery *query =
            qobject_cast<QmlJsDebugClient::QDeclarativeDebugExpressionQuery *>(
                sender());
1024 1025 1026 1027 1028
    if (query && state != QmlJsDebugClient::QDeclarativeDebugQuery::Error) {
        QtMessageLogItem *item = constructLogItemTree(query->result());
        if (item)
            qtMessageLogHandler()->appendItem(item);
    } else