qmlengine.cpp 13.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmlengine.h"

32
#include "debuggerconstants.h"
33
#include "debuggerplugin.h"
34
#include "debuggerdialogs.h"
35
#include "debuggerstringutils.h"
36
#include "debuggeruiswitcher.h"
37

38 39 40 41 42 43 44
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include "watchutils.h"

45 46
#include <projectexplorer/environment.h>

47 48 49 50 51 52 53 54 55 56 57 58 59
#include <utils/qtcassert.h>

#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>

#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QToolTip>
60
#include <QtGui/QTextDocument>
61 62

#include <QtNetwork/QTcpSocket>
63 64
#include <QtNetwork/QHostAddress>

65 66 67 68 69 70 71 72 73
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s


74

75 76 77
namespace Debugger {
namespace Internal {

78 79 80 81 82 83
QDataStream& operator>>(QDataStream& s, WatchData &data)
{
    data = WatchData();
    QString value;
    QString type;
    bool hasChildren;
84
    s >> data.exp >> data.name >> value >> type >> hasChildren >> data.objectId;
85 86 87
    data.setType(type, false);
    data.setValue(value);
    data.setHasChildren(hasChildren);
88
    data.setAllUnneeded();
89 90 91
    return s;
}

92 93 94 95 96 97
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

98
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
99
    : DebuggerEngine(startParameters), m_ping(0)
100 101 102 103 104 105 106
{
}

QmlEngine::~QmlEngine()
{
}

hjk's avatar
hjk committed
107
void QmlEngine::setupInferior()
108
{
hjk's avatar
hjk committed
109 110 111 112
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
    attemptBreakpointSynchronization();
    notifyInferiorSetupOk();
}
hjk's avatar
hjk committed
113

hjk's avatar
hjk committed
114 115 116
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
117
    notifyEngineRunAndInferiorRunOk();
118 119
}

hjk's avatar
hjk committed
120
void QmlEngine::shutdownInferior()
121
{
hjk's avatar
hjk committed
122 123 124 125 126 127 128 129 130 131
    QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
    QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
    //m_objectTreeWidget->saveSettings(m_settings);
    //m_propertiesWidget->saveSettings(m_settings);
    //m_settings.saveSettings(Core::ICore::instance()->settings());
132 133
}

134 135
const int serverPort = 3768;

hjk's avatar
hjk committed
136
void QmlEngine::setupEngine()
137
{
138
    notifyEngineSetupOk();
139 140
}

141 142 143

void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
144
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
145 146 147 148
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("CONTINUE");
    sendMessage(reply);
149
    resetLocation();
hjk's avatar
hjk committed
150 151
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
152 153 154 155
}

void QmlEngine::interruptInferior()
{
156 157 158 159
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("INTERRUPT");
    sendMessage(reply);
160
    notifyInferiorStopOk();
161 162 163 164
}

void QmlEngine::executeStep()
{
165 166 167 168
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("STEPINTO");
    sendMessage(reply);
hjk's avatar
hjk committed
169 170
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
171 172 173 174
}

void QmlEngine::executeStepI()
{
175 176 177 178
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("STEPINTO");
    sendMessage(reply);
hjk's avatar
hjk committed
179 180
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
181 182 183 184
}

void QmlEngine::executeStepOut()
{
185 186 187 188
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("STEPOUT");
    sendMessage(reply);
hjk's avatar
hjk committed
189 190
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
191 192 193 194
}

void QmlEngine::executeNext()
{
195 196 197 198
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("STEPOVER");
    sendMessage(reply);
hjk's avatar
hjk committed
199 200
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
201 202 203 204
}

void QmlEngine::executeNextI()
{
205
    SDEBUG("QmlEngine::executeNextI()");
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
}

void QmlEngine::executeRunToLine(const QString &fileName, int lineNumber)
{
    Q_UNUSED(fileName)
    Q_UNUSED(lineNumber)
    SDEBUG("FIXME:  QmlEngine::executeRunToLine()");
}

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

void QmlEngine::executeJumpToLine(const QString &fileName, int lineNumber)
{
    Q_UNUSED(fileName)
    Q_UNUSED(lineNumber)
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
    Q_UNUSED(index)
231 232 233 234 235 236 237

    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("ACTIVATE_FRAME");
    rs << index;
    sendMessage(reply);

238
    gotoLocation(stackHandler()->frames().value(index), true);
239 240 241 242 243 244 245 246 247
}

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

void QmlEngine::attemptBreakpointSynchronization()
{
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    BreakHandler *handler = breakHandler();
    //bool updateNeeded = false;
    QSet< QPair<QString, qint32> > breakList;
    for (int index = 0; index != handler->size(); ++index) {
        BreakpointData *data = handler->at(index);
        breakList << qMakePair(data->fileName, data->lineNumber.toInt());
    }

    {
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("BREAKPOINTS");
    rs << breakList;
    //qDebug() << Q_FUNC_INFO << breakList;
    sendMessage(reply);
    }
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 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
}

void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

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

static WatchData m_toolTip;
static QPoint m_toolTipPos;
static QHash<QString, WatchData> m_toolTipCache;

void QmlEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
    Q_UNUSED(mousePos)
    Q_UNUSED(editor)
    Q_UNUSED(cursorPos)
}

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

void QmlEngine::assignValueInDebugger(const QString &expression,
    const QString &value)
{
310 311 312 313 314 315 316 317 318 319 320 321 322
    QRegExp inObject("@([0-9a-fA-F]+)->(.+)");
    if (inObject.exactMatch(expression)) {
        bool ok = false;
        quint64 objectId = inObject.cap(1).toULongLong(&ok, 16);
        QString property = inObject.cap(2);
        if (ok && objectId > 0 && !property.isEmpty()) {
            QByteArray reply;
            QDataStream rs(&reply, QIODevice::WriteOnly);
            rs << QByteArray("SET_PROPERTY");
            rs << expression.toUtf8() << objectId << property << value;
            sendMessage(reply);
        }
    }
323 324
}

325
void QmlEngine::updateWatchData(const WatchData &data)
326
{
Olivier Goffart's avatar
Olivier Goffart committed
327
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
328
    //watchHandler()->rebuildModel();
329
    showStatusMessage(tr("Stopped."), 5000);
330

331
    if (!data.name.isEmpty() && data.isValueNeeded()) {
332 333 334 335 336 337 338
        QByteArray reply;
        QDataStream rs(&reply, QIODevice::WriteOnly);
        rs << QByteArray("EXEC");
        rs << data.iname << data.name;
        sendMessage(reply);
    }

339 340 341 342
    if (!data.name.isEmpty() && data.isChildrenNeeded() && watchHandler()->isExpandedIName(data.iname)) {
        expandObject(data.iname, data.objectId);
    }

343 344 345 346 347 348 349
    {
        QByteArray reply;
        QDataStream rs(&reply, QIODevice::WriteOnly);
        rs << QByteArray("WATCH_EXPRESSIONS");
        rs << watchHandler()->watchedExpressions();
        sendMessage(reply);
    }
350 351 352

    if (!data.isSomethingNeeded())
        watchHandler()->insertData(data);
353 354
}

355 356 357 358 359 360 361 362 363
void QmlEngine::expandObject(const QByteArray& iname, quint64 objectId)
{
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("EXPAND");
    rs << iname << objectId;
    sendMessage(reply);
}

364 365 366 367 368 369 370 371 372 373 374
void QmlEngine::sendPing()
{
    m_ping++;
    QByteArray reply;
    QDataStream rs(&reply, QIODevice::WriteOnly);
    rs << QByteArray("PING");
    rs << m_ping;
    sendMessage(reply);
}


375

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
DebuggerEngine *createQmlEngine(const DebuggerStartParameters &sp)
{
    return new QmlEngine(sp);
}

unsigned QmlEngine::debuggerCapabilities() const
{
    return AddWatcherCapability;
    /*ReverseSteppingCapability | SnapshotCapability
        | AutoDerefPointersCapability | DisassemblerCapability
        | RegisterCapability | ShowMemoryCapability
        | JumpToLineCapability | ReloadModuleCapability
        | ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
        | ReturnFromFunctionCapability
        | CreateFullBacktraceCapability
        | WatchpointCapability
        | AddWatcherCapability;*/
393 394 395 396 397 398 399 400 401 402
}

void QmlEngine::messageReceived(const QByteArray &message)
{
    QByteArray rwData = message;
    QDataStream stream(&rwData, QIODevice::ReadOnly);

    QByteArray command;
    stream >> command;

403
    showMessage(_("RECEIVED RESPONSE: ") + quoteUnprintableLatin1(message));
404
    if (command == "STOPPED") {
hjk's avatar
hjk committed
405
        notifyInferiorSpontaneousStop();
406

407
        QList<QPair<QString, QPair<QString, qint32> > > backtrace;
408 409
        QList<WatchData> watches;
        QList<WatchData> locals;
410
        stream >> backtrace >> watches >> locals;
411

412
        StackFrames stackFrames;
413 414 415 416 417 418
        typedef QPair<QString, QPair<QString, qint32> > Iterator;
        foreach (const Iterator &it, backtrace) {
            StackFrame frame;
            frame.file = it.second.first;
            frame.line = it.second.second;
            frame.function = it.first;
419
            stackFrames.append(frame);
420 421 422 423 424 425
        }

        gotoLocation(stackFrames.value(0), true);
        stackHandler()->setFrames(stackFrames);

        watchHandler()->beginCycle();
426
        bool needPing = false;
427

428
        foreach (WatchData data, watches) {
429
            data.iname = watchHandler()->watcherName(data.exp);
430
            watchHandler()->insertData(data);
431

432 433
            if (watchHandler()->expandedINames().contains(data.iname)) {
                needPing = true;
434
                expandObject(data.iname, data.objectId);
435
            }
436 437
        }

438 439 440
        foreach (WatchData data, locals) {
            data.iname = "local." + data.exp;
            watchHandler()->insertData(data);
441

442 443
            if (watchHandler()->expandedINames().contains(data.iname)) {
                needPing = true;
444
                expandObject(data.iname, data.objectId);
445
            }
hjk's avatar
hjk committed
446 447
        }

448 449 450 451
        if (needPing)
            sendPing();
        else
            watchHandler()->endCycle();
452

453 454 455 456
        //ensure we got the right ui right now
        Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance();
        uiSwitcher->setActiveLanguage("C++");

457 458 459 460 461 462 463 464 465 466
        bool becauseOfexception;
        stream >> becauseOfexception;
        if (becauseOfexception) {
            QString error;
            stream >> error;

            QString msg = tr("<p>An Uncaught Exception occured in <i>%1</i>:</p><p>%2</p>").arg(stackFrames.value(0).file, Qt::escape(error));
            showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg);
        }

467

468 469
    } else if (command == "RESULT") {
        WatchData data;
470 471 472
        QByteArray iname;
        stream >> iname >> data;
        data.iname = iname;
473
        watchHandler()->insertData(data);
474 475 476 477 478

    } else if (command == "EXPANDED") {
        QList<WatchData> result;
        QByteArray iname;
        stream >> iname >> result;
479
        bool needPing = false;
480 481 482 483
        foreach (WatchData data, result) {
            data.iname = iname + '.' + data.exp;
            watchHandler()->insertData(data);

484 485
            if (watchHandler()->expandedINames().contains(data.iname)) {
                needPing = true;
486
                expandObject(data.iname, data.objectId);
487
            }
488
        }
489 490
        if (needPing)
            sendPing();
491 492 493 494 495
    } else if (command == "LOCALS") {
        QList<WatchData> locals;
        int frameId;
        stream >> frameId >> locals;
        watchHandler()->beginCycle();
496
        bool needPing = false;
497 498 499
        foreach (WatchData data, locals) {
            data.iname = "local." + data.exp;
            watchHandler()->insertData(data);
500 501
            if (watchHandler()->expandedINames().contains(data.iname)) {
                needPing = true;
502
                expandObject(data.iname, data.objectId);
503
            }
504
        }
505 506 507 508 509 510 511 512 513 514
        if (needPing)
            sendPing();
        else
            watchHandler()->endCycle();

    } else if (command == "PONG") {
        int ping;
        stream >> ping;
        if (ping == m_ping)
            watchHandler()->endCycle();
515 516 517 518 519 520
    } else {
        qDebug() << Q_FUNC_INFO << "Unknown command: " << command;
    }

}

521 522
} // namespace Internal
} // namespace Debugger
523