/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qqmldebugservice_p.h" #include "qqmldebugservice_p_p.h" #include "qqmldebugserver_p.h" #include #include #include #include #include QT_BEGIN_NAMESPACE QQmlDebugServicePrivate::QQmlDebugServicePrivate() : server(0) { } QQmlDebugService::QQmlDebugService(const QString &name, float version, QObject *parent) : QObject(*(new QQmlDebugServicePrivate), parent) { Q_D(QQmlDebugService); d->name = name; d->version = version; d->server = QQmlDebugServer::instance(); d->state = QQmlDebugService::NotConnected; } QQmlDebugService::QQmlDebugService(QQmlDebugServicePrivate &dd, const QString &name, float version, QObject *parent) : QObject(dd, parent) { Q_D(QQmlDebugService); d->name = name; d->version = version; d->server = QQmlDebugServer::instance(); d->state = QQmlDebugService::NotConnected; } /** Registers the service. This should be called in the constructor of the inherited class. From then on the service might get asynchronous calls to messageReceived(). */ QQmlDebugService::State QQmlDebugService::registerService() { Q_D(QQmlDebugService); if (!d->server) return NotConnected; if (d->server->serviceNames().contains(d->name)) { qWarning() << "QQmlDebugService: Conflicting plugin name" << d->name; d->server = 0; } else { d->server->addService(this); } return state(); } QQmlDebugService::~QQmlDebugService() { Q_D(const QQmlDebugService); if (d->server) { d->server->removeService(this); } } QString QQmlDebugService::name() const { Q_D(const QQmlDebugService); return d->name; } float QQmlDebugService::version() const { Q_D(const QQmlDebugService); return d->version; } QQmlDebugService::State QQmlDebugService::state() const { Q_D(const QQmlDebugService); return d->state; } namespace { struct ObjectReference { QPointer object; int id; }; struct ObjectReferenceHash { ObjectReferenceHash() : nextId(0) {} QHash objects; QHash ids; int nextId; }; } Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash) /*! Returns a unique id for \a object. Calling this method multiple times for the same object will return the same id. */ int QQmlDebugService::idForObject(QObject *object) { if (!object) return -1; ObjectReferenceHash *hash = objectReferenceHash(); QHash::Iterator iter = hash->objects.find(object); if (iter == hash->objects.end()) { int id = hash->nextId++; hash->ids.insert(id, object); iter = hash->objects.insert(object, ObjectReference()); iter->object = object; iter->id = id; } else if (iter->object != object) { int id = hash->nextId++; hash->ids.remove(iter->id); hash->ids.insert(id, object); iter->object = object; iter->id = id; } return iter->id; } /*! Returns the object for unique \a id. If the object has not previously been assigned an id, through idForObject(), then 0 is returned. If the object has been destroyed, 0 is returned. */ QObject *QQmlDebugService::objectForId(int id) { ObjectReferenceHash *hash = objectReferenceHash(); QHash::Iterator iter = hash->ids.find(id); if (iter == hash->ids.end()) return 0; QHash::Iterator objIter = hash->objects.find(*iter); Q_ASSERT(objIter != hash->objects.end()); if (objIter->object == 0) { hash->ids.erase(iter); hash->objects.erase(objIter); // run a loop to remove other invalid objects removeInvalidObjectsFromHash(); return 0; } else { return *iter; } } /*! Returns a list of objects matching the given filename, line and column. */ QList QQmlDebugService::objectForLocationInfo(const QString &filename, int lineNumber, int columnNumber) { ObjectReferenceHash *hash = objectReferenceHash(); QList objects; QHash::Iterator iter = hash->ids.begin(); while (iter != hash->ids.end()) { QHash::Iterator objIter = hash->objects.find(*iter); Q_ASSERT(objIter != hash->objects.end()); if (objIter->object == 0) { iter = hash->ids.erase(iter); hash->objects.erase(objIter); } else { QQmlData *ddata = QQmlData::get(iter.value()); if (ddata && ddata->outerContext) { if (QFileInfo(ddata->outerContext->urlString).fileName() == filename && ddata->lineNumber == lineNumber && ddata->columnNumber >= columnNumber) { objects << *iter; } } ++iter; } } return objects; } void QQmlDebugService::removeInvalidObjectsFromHash() { ObjectReferenceHash *hash = objectReferenceHash(); QHash::Iterator iter = hash->ids.begin(); while (iter != hash->ids.end()) { QHash::Iterator objIter = hash->objects.find(*iter); Q_ASSERT(objIter != hash->objects.end()); if (objIter->object == 0) { iter = hash->ids.erase(iter); hash->objects.erase(objIter); } else { ++iter; } } } void QQmlDebugService::clearObjectsFromHash() { ObjectReferenceHash *hash = objectReferenceHash(); hash->ids.clear(); hash->objects.clear(); } bool QQmlDebugService::isDebuggingEnabled() { return QQmlDebugServer::instance() != 0; } bool QQmlDebugService::hasDebuggingClient() { return QQmlDebugServer::instance() != 0 && QQmlDebugServer::instance()->hasDebuggingClient(); } bool QQmlDebugService::blockingMode() { return QQmlDebugServer::instance() != 0 && QQmlDebugServer::instance()->blockingMode(); } QString QQmlDebugService::objectToString(QObject *obj) { if(!obj) return QStringLiteral("NULL"); QString objectName = obj->objectName(); if(objectName.isEmpty()) objectName = QStringLiteral(""); QString rv = QString::fromUtf8(obj->metaObject()->className()) + QLatin1String(": ") + objectName; return rv; } void QQmlDebugService::sendMessage(const QByteArray &message) { sendMessages(QList() << message); } void QQmlDebugService::sendMessages(const QList &messages) { Q_D(QQmlDebugService); if (state() != Enabled) return; d->server->sendMessages(this, messages); } void QQmlDebugService::stateAboutToBeChanged(State) { } void QQmlDebugService::stateChanged(State) { } void QQmlDebugService::messageReceived(const QByteArray &) { } QQmlDebugStream::QQmlDebugStream() : QDataStream() { setVersion(QQmlDebugServer::s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QIODevice *d) : QDataStream(d) { setVersion(QQmlDebugServer::s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags) : QDataStream(ba, flags) { setVersion(QQmlDebugServer::s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(const QByteArray &ba) : QDataStream(ba) { setVersion(QQmlDebugServer::s_dataStreamVersion); } QT_END_NAMESPACE