diff options
author | Martin Jones <martin.jones@nokia.com> | 2009-05-26 08:34:03 (GMT) |
---|---|---|
committer | Martin Jones <martin.jones@nokia.com> | 2009-05-26 08:34:03 (GMT) |
commit | aedd4d0cfb6965f51fb38689539b3417b6071601 (patch) | |
tree | 5c80c85d0f8d26c45be1d5956d9c903aee95174c /src/declarative | |
parent | b29a83e5070410359ebed2d99b18f86bbe2d2939 (diff) | |
parent | a06c0fe5a2c9eebf5e7cceb06f64e725b42715bd (diff) | |
download | Qt-aedd4d0cfb6965f51fb38689539b3417b6071601.zip Qt-aedd4d0cfb6965f51fb38689539b3417b6071601.tar.gz Qt-aedd4d0cfb6965f51fb38689539b3417b6071601.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Diffstat (limited to 'src/declarative')
40 files changed, 2321 insertions, 1369 deletions
diff --git a/src/declarative/canvas/canvas.pri b/src/declarative/canvas/canvas.pri index 8abfd99..9bdd3fa 100644 --- a/src/declarative/canvas/canvas.pri +++ b/src/declarative/canvas/canvas.pri @@ -2,7 +2,7 @@ SOURCES += \ canvas/qsimplecanvas.cpp \ canvas/qsimplecanvasitem.cpp \ canvas/qsimplecanvasfilter.cpp \ - canvas/qsimplecanvasserver.cpp + canvas/qsimplecanvasdebugplugin.cpp HEADERS += \ canvas/qsimplecanvas.h \ @@ -11,7 +11,7 @@ HEADERS += \ canvas/qsimplecanvas_p.h \ canvas/qsimplecanvasitem_p.h \ canvas/qsimplecanvasfilter_p.h \ - canvas/qsimplecanvasserver_p.h + canvas/qsimplecanvasdebugplugin_p.h contains(QT_CONFIG, opengles2): SOURCES += canvas/qsimplecanvas_opengl.cpp else:contains(QT_CONFIG, opengles1): SOURCES += canvas/qsimplecanvas_opengl1.cpp diff --git a/src/declarative/canvas/monitor/main.cpp b/src/declarative/canvas/monitor/main.cpp deleted file mode 100644 index f5f40bd..0000000 --- a/src/declarative/canvas/monitor/main.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include <QApplication> -#include <QWidget> -#include <QPainter> -#include <QTcpSocket> -#include <QScrollBar> - -class QLineGraph : public QWidget -{ -Q_OBJECT -public: - QLineGraph(QWidget * = 0); - - void setPosition(int); - -public slots: - void addSample(int, int, int, int, bool); - -protected: - virtual void paintEvent(QPaintEvent *); - virtual void mousePressEvent(QMouseEvent *); - virtual void resizeEvent(QResizeEvent *); - virtual void showEvent(QShowEvent *); - -private slots: - void scrollbarChanged(int); - -private: - void positionScrollbar(); - void updateScrollbar(); - void drawSample(QPainter *, int, const QRect &); - void drawTime(QPainter *, const QRect &); - struct Sample { - int sample[4]; - bool isBreak; - }; - QList<Sample> _samples; - - QScrollBar sb; - int position; - int samplesPerWidth; - int resolutionForHeight; - bool ignoreScroll; -}; - -QLineGraph::QLineGraph(QWidget *parent) -: QWidget(parent), sb(Qt::Horizontal, this), position(-1), samplesPerWidth(99), resolutionForHeight(50), ignoreScroll(false) -{ - sb.setMaximum(0); - sb.setMinimum(0); - sb.setSingleStep(1); - - QObject::connect(&sb, SIGNAL(valueChanged(int)), this, SLOT(scrollbarChanged(int))); -} - -void QLineGraph::scrollbarChanged(int v) -{ - if(ignoreScroll) - return; - - position = v; - update(); -} - -void QLineGraph::positionScrollbar() -{ - sb.setFixedWidth(width()); - sb.move(0, height() - sb.height()); -} - -void QLineGraph::resizeEvent(QResizeEvent *e) -{ - QWidget::resizeEvent(e); - positionScrollbar(); -} - -void QLineGraph::showEvent(QShowEvent *e) -{ - QWidget::showEvent(e); - positionScrollbar(); -} - -void QLineGraph::mousePressEvent(QMouseEvent *) -{ - if(position == -1) { - position = qMax(0, _samples.count() - samplesPerWidth - 1); - } else { - position = -1; - } - update(); -} - -void QLineGraph::updateScrollbar() -{ - ignoreScroll = true; - sb.setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); - - if(position == -1) { - sb.setValue(sb.maximum()); - } else { - sb.setValue(position); - } - ignoreScroll = false; -} - -void QLineGraph::addSample(int a, int b, int c, int d, bool isBreak) -{ - Sample s; - s.isBreak = isBreak; - s.sample[0] = a; - s.sample[1] = b; - s.sample[2] = c; - s.sample[3] = d; - _samples << s; - updateScrollbar(); - update(); -} - -void QLineGraph::drawTime(QPainter *p, const QRect &rect) -{ - if(_samples.isEmpty()) - return; - - int first = position; - if(first == -1) - first = qMax(0, _samples.count() - samplesPerWidth - 1); - int last = qMin(_samples.count() - 1, first + samplesPerWidth); - - qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); - - int t = 0; - - for(int ii = first; ii <= last; ++ii) { - int sampleTime = _samples.at(ii).sample[3] / 1000; - if(sampleTime != t) { - - int xEnd = rect.left() + scaleX * (ii - first); - p->drawLine(xEnd, rect.bottom(), xEnd, rect.bottom() + 7); - - QRect text(xEnd - 30, rect.bottom() + 10, 60, 30); - - p->drawText(text, Qt::AlignHCenter | Qt::AlignTop, QString::number(_samples.at(ii).sample[3])); - - t = sampleTime; - } - } - -} - -void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect) -{ - if(_samples.isEmpty()) - return; - - int first = position; - if(first == -1) - first = qMax(0, _samples.count() - samplesPerWidth - 1); - int last = qMin(_samples.count() - 1, first + samplesPerWidth); - - qreal scaleY = rect.height() / resolutionForHeight; - qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); - - int xEnd; - int lastXEnd = rect.left(); - - p->save(); - p->setPen(Qt::NoPen); - for(int ii = first + 1; ii <= last; ++ii) { - - xEnd = rect.left() + scaleX * (ii - first); - int yEnd = rect.bottom() - _samples.at(ii).sample[s] * scaleY; - - if (!(s == 0 && _samples.at(ii).isBreak)) - p->drawRect(QRect(lastXEnd, yEnd, scaleX, _samples.at(ii).sample[s] * scaleY)); - - lastXEnd = xEnd; - } - p->restore(); -} - -void QLineGraph::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - - QRect r(50, 10, width() - 60, height() - 60); - p.setBrush(QColor("lightsteelblue")); - drawSample(&p, 0, r); - - p.setBrush(QColor("pink")); - drawSample(&p, 1, r); - - p.setBrush(QColor("green")); - drawSample(&p, 2, r); - - p.setBrush(Qt::NoBrush); - p.drawRect(r); - - for(int ii = 0; ii <= resolutionForHeight; ++ii) { - int y = 1 + r.bottom() - ii * r.height() / resolutionForHeight; - - if((ii % 10) == 0) { - p.drawLine(r.left() - 20, y, r.left(), y); - QRect text(r.left() - 20 - 53, y - 10, 50, 20); - p.drawText(text, Qt::AlignRight | Qt::AlignVCenter, QString::number(ii)); - } else { - p.drawLine(r.left() - 7, y, r.left(), y); - } - } - - drawTime(&p, r); -} - -class MyReader : public QObject -{ -Q_OBJECT -public: - MyReader(const QString &host, int port); - -signals: - void sample(int, int, int, int, bool); - -private slots: - void readyRead(); - -private: - QTcpSocket *socket; - - int la; - int lb; - int ld; -}; - -MyReader::MyReader(const QString &host, int port) -: socket(0), la(-1) -{ - socket = new QTcpSocket(this); - QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); - socket->connectToHost(host, port); - socket->waitForConnected(); -} - -void MyReader::readyRead() -{ - static int la = -1; - static int lb; - static int ld; - - if(socket->canReadLine()) { - QString line = socket->readLine(); - - int a; - int b; - int c; - int d; - int isBreak; - sscanf(line.toLatin1().constData(), "%d %d %d %d %d", &a, &b, &c, &d, &isBreak); - - if (la != -1) - emit sample(c, lb, la, ld, isBreak); - - la = a; - lb = b; - ld = d; - } -} - -int main(int argc, char ** argv) -{ - if(argc != 3) { - qWarning() << "Usage:" << argv[0] << "host port"; - return -1; - } - - QApplication app(argc, argv); - - MyReader reader(argv[1], atoi(argv[2])); - - QLineGraph graph; - QObject::connect(&reader, SIGNAL(sample(int,int,int,int,bool)), &graph, SLOT(addSample(int,int,int,int,bool))); - graph.setFixedSize(800, 600); - graph.show(); - - return app.exec(); -} - -#include "main.moc" diff --git a/src/declarative/canvas/monitor/monitor.pro b/src/declarative/canvas/monitor/monitor.pro deleted file mode 100644 index e3b0a29..0000000 --- a/src/declarative/canvas/monitor/monitor.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . -QT += network - -# Input -SOURCES += main.cpp diff --git a/src/declarative/canvas/qsimplecanvas.cpp b/src/declarative/canvas/qsimplecanvas.cpp index fbd5014..e582226 100644 --- a/src/declarative/canvas/qsimplecanvas.cpp +++ b/src/declarative/canvas/qsimplecanvas.cpp @@ -53,7 +53,7 @@ #include <glheaders.h> #endif #include "qboxlayout.h" -#include "qsimplecanvasserver_p.h" +#include "qsimplecanvasdebugplugin_p.h" #include "qsimplecanvas.h" @@ -533,8 +533,8 @@ void QSimpleCanvasGraphicsView::paintEvent(QPaintEvent *pe) int frametimer = canvas->frameTimer.elapsed(); gfxCanvasTiming.append(QSimpleCanvasTiming(r, frametimer, canvas->lrpTime, tbf)); canvas->lrpTime = 0; - if (canvas->canvasServer) - canvas->canvasServer->addTiming(canvas->lrpTime, frametimer, tbf); + if (canvas->debugPlugin) + canvas->debugPlugin->addTiming(canvas->lrpTime, frametimer, tbf); } void QSimpleCanvasGraphicsView::focusInEvent(QFocusEvent *) @@ -573,11 +573,9 @@ void QSimpleCanvasPrivate::init(QSimpleCanvas::CanvasMode mode) if (continuousUpdate()) qWarning("QSimpleCanvas: Continuous update enabled"); - QByteArray env = qgetenv("GFX_CANVAS_SERVER_PORT"); - if (!env.isEmpty()){ - int port = env.toInt(); - if (port >= 1024) - canvasServer = new QSimpleCanvasServer(port, q); + if (QmlDebugServerPlugin::isDebuggingEnabled()) { + debugPlugin = new QSimpleCanvasDebugPlugin(q); + new QSimpleCanvasSceneDebugPlugin(q); } root = new QSimpleCanvasRootLayer(q); @@ -950,8 +948,8 @@ bool QSimpleCanvas::event(QEvent *e) int frametimer = d->frameTimer.elapsed(); gfxCanvasTiming.append(QSimpleCanvasTiming(r, frametimer, d->lrpTime, tbf)); - if (d->canvasServer) - d->canvasServer->addTiming(d->lrpTime, frametimer, tbf); + if (d->debugPlugin) + d->debugPlugin->addTiming(d->lrpTime, frametimer, tbf); d->lrpTime = 0; if (continuousUpdate()) queueUpdate(); diff --git a/src/declarative/canvas/qsimplecanvas_p.h b/src/declarative/canvas/qsimplecanvas_p.h index 9c3408e..d9ed4ac 100644 --- a/src/declarative/canvas/qsimplecanvas_p.h +++ b/src/declarative/canvas/qsimplecanvas_p.h @@ -101,12 +101,12 @@ private: }; class QGLFramebufferObject; -class QSimpleCanvasServer; +class QSimpleCanvasDebugPlugin; class QSimpleCanvasPrivate { public: QSimpleCanvasPrivate(QSimpleCanvas *canvas) - : q(canvas), timer(0), root(0), lrpTime(0), canvasServer(0), focusItem(0), + : q(canvas), timer(0), root(0), lrpTime(0), debugPlugin(0), focusItem(0), lastFocusItem(0), lastMouseItem(0), isSetup(false), view(0) #if defined(QFX_RENDER_OPENGL) @@ -139,7 +139,7 @@ public: QTime frameTimer; QTime lrpTimer; - QSimpleCanvasServer *canvasServer; + QSimpleCanvasDebugPlugin *debugPlugin; QStack<QSimpleCanvasItem *> focusPanels; QHash<QSimpleCanvasItem *, QSimpleCanvasItem *> focusPanelData; diff --git a/src/declarative/canvas/qsimplecanvasserver.cpp b/src/declarative/canvas/qsimplecanvasdebugplugin.cpp index ed781b8..f5f4f53 100644 --- a/src/declarative/canvas/qsimplecanvasserver.cpp +++ b/src/declarative/canvas/qsimplecanvasdebugplugin.cpp @@ -39,19 +39,18 @@ ** ****************************************************************************/ -#include "qsimplecanvasserver_p.h" +#include "qsimplecanvasdebugplugin_p.h" #include "qdebug.h" -#ifndef Q_OS_WIN32 -#include <arpa/inet.h> -#endif #include <QtCore/qabstractanimation.h> +#include <qsimplecanvas.h> +#include <qsimplecanvasitem.h> QT_BEGIN_NAMESPACE class FrameBreakAnimation : public QAbstractAnimation { public: - FrameBreakAnimation(QSimpleCanvasServer *s) + FrameBreakAnimation(QSimpleCanvasDebugPlugin *s) : QAbstractAnimation(s), server(s) { start(); @@ -63,72 +62,88 @@ public: } private: - QSimpleCanvasServer *server; + QSimpleCanvasDebugPlugin *server; }; -QSimpleCanvasServer::QSimpleCanvasServer(int port, QObject *parent) -: QObject(parent), _breaks(0), _tcpServer(new QTcpServer(this)) +QSimpleCanvasDebugPlugin::QSimpleCanvasDebugPlugin(QObject *parent) +: QmlDebugServerPlugin("CanvasFrameRate", parent), _breaks(0) { - QObject::connect(_tcpServer, SIGNAL(newConnection()), - this, SLOT(newConnection())); - _time.start(); - new FrameBreakAnimation(this); - if (!_tcpServer->listen(QHostAddress::Any, port)) { - qWarning() << "QSimpleCanvasServer: Cannot listen on port" << port; - return; - } -} - -void QSimpleCanvasServer::newConnection() -{ - QTcpSocket *socket = _tcpServer->nextPendingConnection(); - QObject::connect(socket, SIGNAL(disconnected()), - this, SLOT(disconnected())); - _tcpClients << socket; } -void QSimpleCanvasServer::addTiming(quint32 paint, - quint32 repaint, - quint32 timeBetweenFrames) +void QSimpleCanvasDebugPlugin::addTiming(quint32 paint, + quint32 repaint, + quint32 timeBetweenFrames) { - /* - quint32 data[3]; - data[0] = ::htonl(paint); - data[1] = ::htonl(repaint); - data[2] = ::htonl(timeBetweenFrames); - */ + if (!isEnabled()) + return; bool isFrameBreak = _breaks > 1; _breaks = 0; int e = _time.elapsed(); - QString d = QString::number(paint) + QLatin1String(" ") + QString::number(repaint) + QLatin1String(" ") + QString::number(timeBetweenFrames) + QLatin1String(" ") + QString::number(e) + QLatin1String(" ") + QString::number(isFrameBreak) + QLatin1String("\n"); - QByteArray ba = d.toLatin1(); - - // XXX - for (int ii = 0; ii < _tcpClients.count(); ++ii) -// _tcpClients.at(ii)->write((const char *)data, 12); - _tcpClients.at(ii)->write(ba.constData(), ba.length()); + QByteArray data; + QDataStream ds(&data, QIODevice::WriteOnly); + ds << (int)paint << (int)repaint << (int)timeBetweenFrames << (int)e + << (bool)isFrameBreak; + sendMessage(data); } -void QSimpleCanvasServer::frameBreak() +void QSimpleCanvasDebugPlugin::frameBreak() { _breaks++; } -void QSimpleCanvasServer::disconnected() +QSimpleCanvasSceneDebugPlugin::QSimpleCanvasSceneDebugPlugin(QSimpleCanvas *parent) +: QmlDebugServerPlugin("CanvasScene", parent), m_canvas(parent) +{ +} + +void QSimpleCanvasSceneDebugPlugin::messageReceived(const QByteArray &) +{ + refresh(); +} + +void QSimpleCanvasSceneDebugPlugin::refresh() { - QTcpSocket *socket = static_cast<QTcpSocket *>(sender()); - - for (int ii = 0; ii < _tcpClients.count(); ++ii) { - if (_tcpClients.at(ii) == socket) { - socket->disconnect(); - socket->deleteLater(); - _tcpClients.removeAt(ii); - return; - } + QByteArray data; + QDataStream ds(&data, QIODevice::WriteOnly); + const QList<QSimpleCanvasItem *> &children = m_canvas->root()->children(); + ds << children.count(); + for (int ii = 0; ii < children.count(); ++ii) + refresh(ds, children.at(ii)); + + sendMessage(data); +} + +void QSimpleCanvasSceneDebugPlugin::refresh(QDataStream &ds, + QSimpleCanvasItem *item) +{ + ds << QmlDebugServerPlugin::objectToString(item) << item->x() << item->y() + << item->z() << item->width() << item->height() + << (int)item->transformOrigin() << item->scale() << (int)item->flip() + << item->transform() << item->hasActiveFocus() << (int)item->options(); + + QPixmap pix; + + if(item->options() & QSimpleCanvasItem::HasContents && + item->width() > 0 && item->height() > 0) { + + pix = QPixmap(item->width(), item->height()); + pix.fill(QColor(0,0,0,0)); + QPainter p(&pix); + item->paintContents(p); + } + + ds << pix; + + const QList<QSimpleCanvasItem *> &children = item->children(); + ds << children.count(); + + for(int ii = 0; ii < children.count(); ++ii) + refresh(ds, children.at(ii)); } QT_END_NAMESPACE + diff --git a/src/declarative/canvas/qsimplecanvasserver_p.h b/src/declarative/canvas/qsimplecanvasdebugplugin_p.h index ce04e8d..270b78c 100644 --- a/src/declarative/canvas/qsimplecanvasserver_p.h +++ b/src/declarative/canvas/qsimplecanvasdebugplugin_p.h @@ -39,37 +39,47 @@ ** ****************************************************************************/ -#ifndef QSIMPLECANVASSERVER_P_H -#define QSIMPLECANVASSERVER_P_H +#ifndef QSIMPLECANVASDEBUGPLUGIN_P_H +#define QSIMPLECANVASDEBUGPLUGIN_P_H #include "qobject.h" #include "qtcpserver.h" #include "qtcpsocket.h" #include "qdatetime.h" - +#include <QtDeclarative/qmldebugserver.h> QT_BEGIN_NAMESPACE -class QSimpleCanvasServer : public QObject +class QSimpleCanvasDebugPlugin : public QmlDebugServerPlugin { -Q_OBJECT public: - QSimpleCanvasServer(int port, QObject *parent); + QSimpleCanvasDebugPlugin(QObject *parent = 0); void addTiming(quint32, quint32, quint32); -private Q_SLOTS: - void newConnection(); - void disconnected(); - private: friend class FrameBreakAnimation; void frameBreak(); int _breaks; - - QTcpServer *_tcpServer; - QList<QTcpSocket *> _tcpClients; QTime _time; }; +class QSimpleCanvas; +class QSimpleCanvasItem; +class QSimpleCanvasSceneDebugPlugin : public QmlDebugServerPlugin +{ +public: + QSimpleCanvasSceneDebugPlugin(QSimpleCanvas *parent = 0); + + virtual void messageReceived(const QByteArray &); + +private: + void refresh(); + void refresh(QDataStream &, QSimpleCanvasItem *); + QSimpleCanvas *m_canvas; +}; + + QT_END_NAMESPACE -#endif // GFXCANVASSERVER_P_H + +#endif // QSIMPLECANVASDEBUGPLUGIN_P_H + diff --git a/src/declarative/debugger/debugger.pri b/src/declarative/debugger/debugger.pri index c386d74..aa36675 100644 --- a/src/declarative/debugger/debugger.pri +++ b/src/declarative/debugger/debugger.pri @@ -3,11 +3,15 @@ SOURCES += debugger/qmldebugger.cpp \ debugger/qmlpropertyview.cpp \ debugger/qmlwatches.cpp \ debugger/qmlobjecttree.cpp \ - debugger/qmlcanvasdebugger.cpp + debugger/qpacketprotocol.cpp \ + debugger/qmldebugserver.cpp \ + debugger/qmldebugclient.cpp HEADERS += debugger/qmldebugger.h \ debugger/qmldebuggerstatus.h \ debugger/qmlpropertyview_p.h \ debugger/qmlwatches_p.h \ debugger/qmlobjecttree_p.h \ - debugger/qmlcanvasdebugger_p.h + debugger/qpacketprotocol.h \ + debugger/qmldebugserver.h \ + debugger/qmldebugclient.h diff --git a/src/declarative/debugger/qmlcanvasdebugger.cpp b/src/declarative/debugger/qmlcanvasdebugger.cpp deleted file mode 100644 index 21abd2d..0000000 --- a/src/declarative/debugger/qmlcanvasdebugger.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, 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. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qmlcanvasdebugger_p.h> -#include <private/qmlwatches_p.h> -#include <QtDeclarative/qsimplecanvas.h> -#include <QtGui/qboxlayout.h> -#include <QtGui/qpushbutton.h> -#include <QtGui/qspinbox.h> -#include <QtGui/qsplitter.h> -#include <QtGui/qtreewidget.h> - -#include <QtDeclarative/qfxrect.h> -#include <QtDeclarative/qfximage.h> - -QmlCanvasDebugger::QmlCanvasDebugger(QmlWatches *w, QWidget *parent) -: QWidget(parent), m_watches(w), m_tree(0), m_canvas(0), m_canvasRoot(0), m_debugCanvas(0), - m_selected(0) -{ - QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(0); - setLayout(layout); - QHBoxLayout *hlayout = new QHBoxLayout; - hlayout->setContentsMargins(0,0,0,0); - hlayout->addStretch(2); - hlayout->setSpacing(0); - layout->addLayout(hlayout); - QSpinBox *x = new QSpinBox(this); - x->setSingleStep(50); - x->setMaximum(10000); - x->setMinimum(-10000); - QObject::connect(x, SIGNAL(valueChanged(int)), this, SLOT(setX(int))); - QSpinBox *y = new QSpinBox(this); - y->setSingleStep(50); - y->setMaximum(10000); - y->setMinimum(-10000); - QObject::connect(y, SIGNAL(valueChanged(int)), this, SLOT(setY(int))); - hlayout->addWidget(x); - hlayout->addWidget(y); - QPushButton *pb = new QPushButton(tr("Refresh"), this); - QObject::connect(pb, SIGNAL(clicked()), this, SLOT(refresh())); - hlayout->addWidget(pb); - - QSplitter *splitter = new QSplitter(this); - - m_tree = new QTreeWidget(this); - QObject::connect(m_tree, SIGNAL(itemExpanded(QTreeWidgetItem*)), - this, SLOT(itemExpanded(QTreeWidgetItem*))); - QObject::connect(m_tree, SIGNAL(itemCollapsed(QTreeWidgetItem*)), - this, SLOT(itemCollapsed(QTreeWidgetItem*))); - QObject::connect(m_tree, SIGNAL(itemClicked(QTreeWidgetItem*,int)), - this, SLOT(itemClicked(QTreeWidgetItem*))); - m_canvas = new QSimpleCanvas(QSimpleCanvas::SimpleCanvas, this); - m_canvasRoot = new QSimpleCanvasItem; - m_canvasRoot->setParent(m_canvas->root()); - splitter->addWidget(m_tree); - splitter->addWidget(m_canvas); - splitter->setStretchFactor(1, 2); - layout->addWidget(splitter); -} - -void QmlCanvasDebugger::refresh() -{ - setCanvas(m_debugCanvas); -} - -class QmlCanvasDebuggerItem : public QTreeWidgetItem -{ -public: - QmlCanvasDebuggerItem(QTreeWidget *tree) - : QTreeWidgetItem(tree), me(0), img(0) - { - } - - QmlCanvasDebuggerItem(QTreeWidgetItem *item) - : QTreeWidgetItem(item), me(0), img(0) - { - } - - QPointer<QObject> them; - QFxRect *me; - QFxImage *img; -}; - -void QmlCanvasDebugger::itemExpanded(QTreeWidgetItem *i) -{ - QmlCanvasDebuggerItem *item = static_cast<QmlCanvasDebuggerItem *>(i); - if(item->me) - item->me->setOpacity(1); -} - -void QmlCanvasDebugger::setOpacityRecur(QTreeWidgetItem *i, qreal op) -{ - QmlCanvasDebuggerItem *item = static_cast<QmlCanvasDebuggerItem *>(i); - if(item->img) - item->img->setOpacity(op); - - for(int ii = 0; ii < item->childCount(); ++ii) - setOpacityRecur(item->child(ii), op); -} - -void QmlCanvasDebugger::itemClicked(QTreeWidgetItem *i) -{ - QmlCanvasDebuggerItem *item = static_cast<QmlCanvasDebuggerItem *>(i); - if(item->them) - emit objectClicked(m_watches->objectId(item->them)); - - if(m_selected) { - setOpacityRecur(m_selected, 0); - m_selected = 0; - } - - m_selected = item; - setOpacityRecur(m_selected, 1); -} - -void QmlCanvasDebugger::itemCollapsed(QTreeWidgetItem *i) -{ - QmlCanvasDebuggerItem *item = static_cast<QmlCanvasDebuggerItem *>(i); - if(item->me) - item->me->setOpacity(0); -} - -void QmlCanvasDebugger::clone(QTreeWidgetItem *item, QSimpleCanvasItem *me, QSimpleCanvasItem *them) -{ - const QList<QSimpleCanvasItem *> &children = them->children(); - - foreach(QSimpleCanvasItem *child, children) { - QmlCanvasDebuggerItem *childItem = new QmlCanvasDebuggerItem(item); - childItem->setText(0, QmlWatches::objectToString(child)); - childItem->setExpanded(true); - - QFxRect *rect = new QFxRect; - rect->setParent(me); - rect->setX(child->x()); - rect->setY(child->y()); - rect->setZ(child->z()); - rect->setWidth(child->width()); - rect->setHeight(child->height()); - rect->setTransformOrigin(child->transformOrigin()); - rect->setScale(child->scale()); - rect->setFlip(child->flip()); - rect->setTransform(child->transform()); - - if(child->hasActiveFocus()) - rect->setColor(QColor(0, 0, 0, 10)); - else if(child->options() & QSimpleCanvasItem::IsFocusPanel) - rect->setColor(QColor(0, 255, 0, 10)); - else if(child->options() & QSimpleCanvasItem::IsFocusRealm) - rect->setColor(QColor(0, 0, 255, 10)); - else - rect->setColor(QColor(255, 0, 0, 10)); - - if(child->width() > 0 && child->height() > 0) { - QPixmap pix(child->width(), child->height()); - pix.fill(QColor(0,0,0,0)); - QPainter p(&pix); - child->paintContents(p); - QFxImage *img = new QFxImage; - img->setParent(rect); - img->setWidth(child->width()); - img->setHeight(child->height()); - img->setPixmap(pix); - img->setOpacity(0); - childItem->img = img; - } - - childItem->them = child; - childItem->me = rect; - - clone(childItem, rect, child); - } -} - -void QmlCanvasDebugger::setX(int x) -{ - m_canvasRoot->setX(x); -} - -void QmlCanvasDebugger::setY(int y) -{ - m_canvasRoot->setY(y); -} - -void QmlCanvasDebugger::setCanvas(QSimpleCanvas *canvas) -{ - QList<QSimpleCanvasItem *> children = m_canvasRoot->children(); - qDeleteAll(children); - m_tree->clear(); - m_selected = 0; - - m_debugCanvas = canvas; - if(!m_debugCanvas) - return; - - QTreeWidgetItem *root = new QmlCanvasDebuggerItem(m_tree); - root->setText(0, tr("Root")); - root->setExpanded(true); - clone(root, m_canvasRoot, m_debugCanvas->root()); -} - diff --git a/src/declarative/debugger/qmldebugclient.cpp b/src/declarative/debugger/qmldebugclient.cpp new file mode 100644 index 0000000..85ccf0b --- /dev/null +++ b/src/declarative/debugger/qmldebugclient.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmldebugclient.h" +#include <private/qtcpsocket_p.h> +#include <QtDeclarative/qpacketprotocol.h> +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +class QmlDebugClientPrivate : public QObject +{ + Q_OBJECT +public: + QmlDebugClientPrivate(QmlDebugClient *c); + QmlDebugClient *q; + QPacketProtocol *protocol; + + QStringList enabled; + QHash<QString, QmlDebugClientPlugin *> plugins; +public slots: + void connected(); + void readyRead(); +}; + +QmlDebugClientPrivate::QmlDebugClientPrivate(QmlDebugClient *c) +: QObject(c), q(c), protocol(0) +{ + protocol = new QPacketProtocol(q, this); + QObject::connect(c, SIGNAL(connected()), this, SLOT(connected())); + QObject::connect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +void QmlDebugClientPrivate::connected() +{ + QPacket pack; + pack << QString(QLatin1String("QmlDebugServer")) << enabled; + protocol->send(pack); +} + +void QmlDebugClientPrivate::readyRead() +{ + QPacket pack = protocol->read(); + QString name; QByteArray message; + pack >> name >> message; + + QHash<QString, QmlDebugClientPlugin *>::Iterator iter = + plugins.find(name); + if (iter == plugins.end()) { + qWarning() << "QmlDebugClient: Message received for missing plugin" << name; + } else { + (*iter)->messageReceived(message); + } +} + +QmlDebugClient::QmlDebugClient(QObject *parent) +: QTcpSocket(parent), d(new QmlDebugClientPrivate(this)) +{ +} + +class QmlDebugClientPluginPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlDebugClientPlugin); +public: + QmlDebugClientPluginPrivate(); + + QString name; + QmlDebugClient *client; + bool enabled; +}; + +QmlDebugClientPluginPrivate::QmlDebugClientPluginPrivate() +: client(0), enabled(false) +{ +} + +QmlDebugClientPlugin::QmlDebugClientPlugin(const QString &name, + QmlDebugClient *parent) +: QObject(*(new QmlDebugClientPluginPrivate), parent) +{ + Q_D(QmlDebugClientPlugin); + d->name = name; + d->client = parent; + + if (!d->client) + return; + + if (d->client->d->plugins.contains(name)) { + qWarning() << "QmlDebugClientPlugin: Conflicting plugin name" << name; + d->client = 0; + } else { + d->client->d->plugins.insert(name, this); + } +} + +QString QmlDebugClientPlugin::name() const +{ + Q_D(const QmlDebugClientPlugin); + return d->name; +} + +bool QmlDebugClientPlugin::isEnabled() const +{ + Q_D(const QmlDebugClientPlugin); + return d->enabled; +} + +void QmlDebugClientPlugin::setEnabled(bool e) +{ + Q_D(QmlDebugClientPlugin); + if (e == d->enabled) + return; + + d->enabled = e; + + if (d->client) { + if (e) + d->client->d->enabled.append(d->name); + else + d->client->d->enabled.removeAll(d->name); + + if (d->client->state() == QTcpSocket::ConnectedState) { + QPacket pack; + pack << QString(QLatin1String("QmlDebugServer")); + if (e) pack << (int)1; + else pack << (int)2; + pack << d->name; + d->client->d->protocol->send(pack); + } + } +} + +void QmlDebugClientPlugin::sendMessage(const QByteArray &message) +{ + Q_D(QmlDebugClientPlugin); + + if (!d->client || d->client->state() != QTcpSocket::ConnectedState) + return; + + QPacket pack; + pack << d->name << message; + d->client->d->protocol->send(pack); +} + +void QmlDebugClientPlugin::messageReceived(const QByteArray &) +{ +} + +#include "qmldebugclient.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qmldebugclient.h b/src/declarative/debugger/qmldebugclient.h new file mode 100644 index 0000000..3fbf534 --- /dev/null +++ b/src/declarative/debugger/qmldebugclient.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLDEBUGCLIENT_H +#define QMLDEBUGCLIENT_H + +#include <QtNetwork/qtcpsocket.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlDebugClientPrivate; +class Q_DECLARATIVE_EXPORT QmlDebugClient : public QTcpSocket +{ + Q_OBJECT + Q_DISABLE_COPY(QmlDebugClient) +public: + QmlDebugClient(QObject * = 0); + +private: + QmlDebugClientPrivate *d; + friend class QmlDebugClientPlugin; + friend class QmlDebugClientPluginPrivate; +}; + +class QmlDebugClientPluginPrivate; +class Q_DECLARATIVE_EXPORT QmlDebugClientPlugin : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlDebugClientPlugin) + Q_DISABLE_COPY(QmlDebugClientPlugin) + +public: + QmlDebugClientPlugin(const QString &, QmlDebugClient *parent); + + QString name() const; + + bool isEnabled() const; + void setEnabled(bool); + + void sendMessage(const QByteArray &); + +protected: + virtual void messageReceived(const QByteArray &); + +private: + friend class QmlDebugClient; + friend class QmlDebugClientPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLDEBUGCLIENT_H diff --git a/src/declarative/debugger/qmldebugger.cpp b/src/declarative/debugger/qmldebugger.cpp index 0bbcb2c..c925148 100644 --- a/src/declarative/debugger/qmldebugger.cpp +++ b/src/declarative/debugger/qmldebugger.cpp @@ -61,11 +61,10 @@ #include <QtDeclarative/qmlexpression.h> #include <private/qmlpropertyview_p.h> #include <private/qmlwatches_p.h> -#include <private/qmlcanvasdebugger_p.h> QmlDebugger::QmlDebugger(QWidget *parent) : QWidget(parent), m_tree(0), m_warnings(0), m_watchTable(0), m_watches(0), - m_canvas(0), m_properties(0), m_text(0), m_highlightedItem(0) + m_properties(0), m_text(0), m_highlightedItem(0) { QHBoxLayout *layout = new QHBoxLayout; setLayout(layout); @@ -111,11 +110,6 @@ QmlDebugger::QmlDebugger(QWidget *parent) tabs->addTab(m_properties, tr("Properties")); tabs->setCurrentWidget(m_properties); - m_canvas = new QmlCanvasDebugger(m_watches, this); - QObject::connect(m_canvas, SIGNAL(objectClicked(quint32)), - this, SLOT(highlightObject(quint32))); - tabs->addTab(m_canvas, tr("Canvas")); - splitter->addWidget(tabs); splitter->setStretchFactor(1, 2); @@ -339,7 +333,6 @@ bool operator<(const QPair<quint32, QPair<int, QString> > &lhs, void QmlDebugger::setCanvas(QSimpleCanvas *c) { - m_canvas->setCanvas(c); } void QmlDebugger::setDebugObject(QObject *obj) diff --git a/src/declarative/debugger/qmldebugger.h b/src/declarative/debugger/qmldebugger.h index 4641bce..10b2f9a 100644 --- a/src/declarative/debugger/qmldebugger.h +++ b/src/declarative/debugger/qmldebugger.h @@ -61,7 +61,6 @@ class QmlPropertyView; class QmlWatches; class QmlObjectTree; class QmlContext; -class QmlCanvasDebugger; class QSimpleCanvas; class QmlDebugger : public QWidget { @@ -88,7 +87,6 @@ private: QTreeWidget *m_warnings; QTableView *m_watchTable; QmlWatches *m_watches; - QmlCanvasDebugger *m_canvas; QmlPropertyView *m_properties; QPlainTextEdit *m_text; QPointer<QObject> m_object; diff --git a/src/declarative/debugger/qmldebugserver.cpp b/src/declarative/debugger/qmldebugserver.cpp new file mode 100644 index 0000000..a253e8c --- /dev/null +++ b/src/declarative/debugger/qmldebugserver.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmldebugserver.h" +#include <QtCore/qdebug.h> +#include <QtNetwork/qtcpserver.h> +#include <QtNetwork/qtcpsocket.h> +#include <private/qobject_p.h> +#include <QtDeclarative/qpacketprotocol.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +class QmlDebugServerPrivate; +class QmlDebugServer : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlDebugServer) + Q_DISABLE_COPY(QmlDebugServer) +public: + static QmlDebugServer *instance(); + +private slots: + void readyRead(); + +private: + friend class QmlDebugServerPlugin; + friend class QmlDebugServerPluginPrivate; + QmlDebugServer(int); +}; + +class QmlDebugServerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlDebugServer); +public: + QmlDebugServerPrivate(); + void init(int port); + + QTcpSocket *connection; + QPacketProtocol *protocol; + QHash<QString, QmlDebugServerPlugin *> plugins; + QStringList enabledPlugins; +}; + +class QmlDebugServerPluginPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlDebugServerPlugin); +public: + QmlDebugServerPluginPrivate(); + + QString name; + QmlDebugServer *server; +}; + +QmlDebugServerPrivate::QmlDebugServerPrivate() +: connection(0), protocol(0) +{ +} + +void QmlDebugServerPrivate::init(int port) +{ + Q_Q(QmlDebugServer); + QTcpServer server; + + if (!server.listen(QHostAddress::Any, port)) { + qWarning("QmlDebugServer: Unable to listen on port %d", port); + return; + } + + qWarning("QmlDebugServer: Waiting for connection on port %d...", port); + + if (!server.waitForNewConnection(-1)) { + qWarning("QmlDebugServer: Connection error"); + return; + } + + connection = server.nextPendingConnection(); + connection->setParent(q); + protocol = new QPacketProtocol(connection, q); + + // ### Wait for hello + while (!protocol->packetsAvailable()) { + connection->waitForReadyRead(); + } + QPacket hello = protocol->read(); + QString name; + hello >> name >> enabledPlugins; + if (name != QLatin1String("QmlDebugServer")) { + qWarning("QmlDebugServer: Invalid hello message"); + delete protocol; delete connection; connection = 0; protocol = 0; + return; + } + + QObject::connect(protocol, SIGNAL(readyRead()), q, SLOT(readyRead())); + q->readyRead(); + + qWarning("QmlDebugServer: Connection established"); +} + +QmlDebugServer *QmlDebugServer::instance() +{ + static bool envTested = false; + static QmlDebugServer *server = 0; + + if (!envTested) { + envTested = true; + QByteArray env = qgetenv("QML_DEBUG_SERVER_PORT"); + + bool ok = false; + int port = env.toInt(&ok); + + if (ok && port > 1024) + server = new QmlDebugServer(port); + } + + if (server && server->d_func()->connection) + return server; + else + return 0; +} + +QmlDebugServer::QmlDebugServer(int port) +: QObject(*(new QmlDebugServerPrivate)) +{ + Q_D(QmlDebugServer); + d->init(port); +} + +void QmlDebugServer::readyRead() +{ + Q_D(QmlDebugServer); + + QString debugServer(QLatin1String("QmlDebugServer")); + + while (d->protocol->packetsAvailable()) { + QPacket pack = d->protocol->read(); + + QString name; + pack >> name; + + if (name == debugServer) { + int op = -1; QString plugin; + pack >> op >> plugin; + + if (op == 1) { + // Enable + if (!d->enabledPlugins.contains(plugin)) { + d->enabledPlugins.append(plugin); + QHash<QString, QmlDebugServerPlugin *>::Iterator iter = + d->plugins.find(plugin); + if (iter != d->plugins.end()) + (*iter)->enabledChanged(true); + } + + } else if (op == 2) { + // Disable + if (d->enabledPlugins.contains(plugin)) { + d->enabledPlugins.removeAll(plugin); + QHash<QString, QmlDebugServerPlugin *>::Iterator iter = + d->plugins.find(plugin); + if (iter != d->plugins.end()) + (*iter)->enabledChanged(false); + } + } else { + qWarning("QmlDebugServer: Invalid control message %d", op); + } + + } else { + QByteArray message; + pack >> message; + + QHash<QString, QmlDebugServerPlugin *>::Iterator iter = + d->plugins.find(name); + if (iter == d->plugins.end()) { + qWarning() << "QmlDebugServer: Message received for missing plugin" << name; + } else { + (*iter)->messageReceived(message); + } + } + } +} + +QmlDebugServerPluginPrivate::QmlDebugServerPluginPrivate() +: server(0) +{ +} + +QmlDebugServerPlugin::QmlDebugServerPlugin(const QString &name, QObject *parent) +: QObject(*(new QmlDebugServerPluginPrivate), parent) +{ + Q_D(QmlDebugServerPlugin); + d->name = name; + d->server = QmlDebugServer::instance(); + + if (!d->server) + return; + + if (d->server->d_func()->plugins.contains(name)) { + qWarning() << "QmlDebugServerPlugin: Conflicting plugin name" << name; + d->server = 0; + } else { + d->server->d_func()->plugins.insert(name, this); + } +} + +QString QmlDebugServerPlugin::name() const +{ + Q_D(const QmlDebugServerPlugin); + return d->name; +} + +bool QmlDebugServerPlugin::isEnabled() const +{ + Q_D(const QmlDebugServerPlugin); + return (d->server && d->server->d_func()->enabledPlugins.contains(d->name)); +} + +bool QmlDebugServerPlugin::isDebuggingEnabled() +{ + return QmlDebugServer::instance() != 0; +} + +QString QmlDebugServerPlugin::objectToString(QObject *obj) +{ + if(!obj) + return QLatin1String("NULL"); + + QString objectName = obj->objectName(); + if(objectName.isEmpty()) + objectName = QLatin1String("<unnamed>"); + + QString rv = QLatin1String(obj->metaObject()->className()) + + QLatin1String(": ") + objectName; + + return rv; +} + +void QmlDebugServerPlugin::sendMessage(const QByteArray &message) +{ + Q_D(QmlDebugServerPlugin); + + if (!d->server || !d->server->d_func()->connection) + return; + + QPacket pack; + pack << d->name << message; + d->server->d_func()->protocol->send(pack); +} + +void QmlDebugServerPlugin::enabledChanged(bool) +{ +} + +void QmlDebugServerPlugin::messageReceived(const QByteArray &) +{ +} + +#include "qmldebugserver.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qmlcanvasdebugger_p.h b/src/declarative/debugger/qmldebugserver.h index 80a2322..02fe7d1 100644 --- a/src/declarative/debugger/qmlcanvasdebugger_p.h +++ b/src/declarative/debugger/qmldebugserver.h @@ -39,51 +39,44 @@ ** ****************************************************************************/ -#ifndef QMLCANVASDEBUGGER_P_H -#define QMLCANVASDEBUGGER_P_H +#ifndef QMLDEBUGSERVER_H +#define QMLDEBUGSERVER_H -#include <QtGui/qwidget.h> -#include <QtCore/qpointer.h> +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QSimpleCanvas; -class QSimpleCanvasItem; -class QTreeWidget; -class QTreeWidgetItem; -class QmlWatches; -class QmlCanvasDebugger : public QWidget +class QmlDebugServerPluginPrivate; +class QmlDebugServerPlugin : public QObject { Q_OBJECT + Q_DECLARE_PRIVATE(QmlDebugServerPlugin) + Q_DISABLE_COPY(QmlDebugServerPlugin) public: - QmlCanvasDebugger(QmlWatches *, QWidget *parent = 0); + QmlDebugServerPlugin(const QString &, QObject *parent = 0); - void setCanvas(QSimpleCanvas *); + QString name() const; -signals: - void objectClicked(quint32); + bool isEnabled() const; -private slots: - void refresh(); - void itemClicked(QTreeWidgetItem *); - void itemExpanded(QTreeWidgetItem *); - void itemCollapsed(QTreeWidgetItem *); - void setX(int); - void setY(int); + void sendMessage(const QByteArray &); -private: - void setOpacityRecur(QTreeWidgetItem *, qreal); - void clone(QTreeWidgetItem *, QSimpleCanvasItem *me, QSimpleCanvasItem *them); + static bool isDebuggingEnabled(); + static QString objectToString(QObject *obj); + +protected: + virtual void enabledChanged(bool); + virtual void messageReceived(const QByteArray &); - QmlWatches *m_watches; - QTreeWidget *m_tree; - QSimpleCanvas *m_canvas; - QSimpleCanvasItem *m_canvasRoot; - QPointer<QSimpleCanvas> m_debugCanvas; - QTreeWidgetItem *m_selected; +private: + friend class QmlDebugServer; }; QT_END_NAMESPACE -#endif // QMLCANVASDEBUGGER_P_H +QT_END_HEADER + +#endif // QMLDEBUGSERVER_H diff --git a/src/declarative/debugger/qpacketprotocol.cpp b/src/declarative/debugger/qpacketprotocol.cpp new file mode 100644 index 0000000..6911b89 --- /dev/null +++ b/src/declarative/debugger/qpacketprotocol.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qpacketprotocol.h" +#include <QBuffer> + +#define MAX_PACKET_SIZE 0x7FFFFFFF + +/*! + \class QPacketProtocol + + \brief The QPacketProtocol class encapsulates communicating discrete packets + across fragmented IO channels, such as TCP sockets. + + QPacketProtocol makes it simple to send arbitrary sized data "packets" across + fragmented transports such as TCP and UDP. + + As transmission boundaries are not respected, sending packets over protocols + like TCP frequently involves "stitching" them back together at the receiver. + QPacketProtocol makes this easier by performing this task for you. Packet + data sent using QPacketProtocol is prepended with a 4-byte size header + allowing the receiving QPacketProtocol to buffer the packet internally until + it has all been received. QPacketProtocol does not perform any sanity + checking on the size or on the data, so this class should only be used in + prototyping or trusted situations where DOS attacks are unlikely. + + QPacketProtocol does not perform any communications itself. Instead it can + operate on any QIODevice that supports the QIODevice::readyRead() signal. A + logical "packet" is encapsulated by the companion QPacket class. The + following example shows two ways to send data using QPacketProtocol. The + transmitted data is equivalent in both. + + \code + QTcpSocket socket; + // ... connect socket ... + + QPacketProtocol protocol(&socket); + + // Send packet the quick way + protocol.send() << "Hello world" << 123; + + // Send packet the longer way + QPacket packet; + packet << "Hello world" << 123; + protocol.send(packet); + \endcode + + Likewise, the following shows how to read data from QPacketProtocol, assuming + that the QPacketProtocol::readyRead() signal has been emitted. + + \code + // ... QPacketProtocol::readyRead() is emitted ... + + int a; + QByteArray b; + + // Receive packet the quick way + protocol.read() >> a >> b; + + // Receive packet the longer way + QPacket packet = protocol.read(); + p >> a >> b; + \endcode + + \ingroup io + \sa QPacket +*/ + +class QPacketProtocolPrivate : public QObject +{ +Q_OBJECT +public: + QPacketProtocolPrivate(QPacketProtocol * parent, QIODevice * _dev) + : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), + dev(_dev) + { + Q_ASSERT(4 == sizeof(qint32)); + + QObject::connect(this, SIGNAL(readyRead()), + parent, SIGNAL(readyRead())); + QObject::connect(this, SIGNAL(packetWritten()), + parent, SIGNAL(packetWritten())); + QObject::connect(this, SIGNAL(invalidPacket()), + parent, SIGNAL(invalidPacket())); + QObject::connect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::connect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::connect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + } + +signals: + void readyRead(); + void packetWritten(); + void invalidPacket(); + +public slots: + void aboutToClose() + { + inProgress.clear(); + sendingPackets.clear(); + inProgressSize = -1; + } + + void bytesWritten(qint64 bytes) + { + Q_ASSERT(!sendingPackets.isEmpty()); + + while(bytes) { + if(sendingPackets.at(0) > bytes) { + sendingPackets[0] -= bytes; + bytes = 0; + } else { + bytes -= sendingPackets.at(0); + sendingPackets.removeFirst(); + emit packetWritten(); + } + } + } + + void readyToRead() + { + if(-1 == inProgressSize) { + // We need a size header of sizeof(qint32) + if(sizeof(qint32) > dev->bytesAvailable()) + return; + + // Read size header + int read = dev->read((char *)&inProgressSize, sizeof(qint32)); + Q_ASSERT(read == sizeof(qint32)); + Q_UNUSED(read); + + // Check sizing constraints + if(inProgressSize > maxPacketSize) { + QObject::disconnect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::disconnect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + dev = 0; + emit invalidPacket(); + return; + } + + inProgressSize -= sizeof(qint32); + + // Need to get trailing data + readyToRead(); + } else { + inProgress.append(dev->read(inProgressSize - inProgress.size())); + + if(inProgressSize == inProgress.size()) { + // Packet has arrived! + packets.append(inProgress); + inProgressSize = -1; + inProgress.clear(); + + emit readyRead(); + + // Need to get trailing data + readyToRead(); + } + } + } + +public: + QList<qint64> sendingPackets; + QList<QByteArray> packets; + QByteArray inProgress; + qint32 inProgressSize; + qint32 maxPacketSize; + QIODevice * dev; +}; + +/*! + Construct a QPacketProtocol instance that works on \a dev with the + specified \a parent. + */ +QPacketProtocol::QPacketProtocol(QIODevice * dev, QObject * parent) +: QObject(parent), d(new QPacketProtocolPrivate(this, dev)) +{ + Q_ASSERT(dev); +} + +/*! + Destroys the QPacketProtocol instance. + */ +QPacketProtocol::~QPacketProtocol() +{ +} + +/*! + Returns the maximum packet size allowed. By default this is + 2,147,483,647 bytes. + + If a packet claiming to be larger than the maximum packet size is received, + the QPacketProtocol::invalidPacket() signal is emitted. + + \sa QPacketProtocol::setMaximumPacketSize() + */ +qint32 QPacketProtocol::maximumPacketSize() const +{ + return d->maxPacketSize; +} + +/*! + Sets the maximum allowable packet size to \a max. + + \sa QPacketProtocol::maximumPacketSize() + */ +qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) +{ + if(max > (signed)sizeof(qint32)) + d->maxPacketSize = max; + return d->maxPacketSize; +} + +/*! + Returns a streamable object that is transmitted on destruction. For example + + \code + protocol.send() << "Hello world" << 123; + \endcode + + will send a packet containing "Hello world" and 123. To construct more + complex packets, explicitly construct a QPacket instance. + */ +QPacketAutoSend QPacketProtocol::send() +{ + return QPacketAutoSend(this); +} + +/*! + \fn void QPacketProtocol::send(const QPacket & packet) + + Transmit the \a packet. + */ +void QPacketProtocol::send(const QPacket & p) +{ + if(p.b.isEmpty()) + return; // We don't send empty packets + + qint64 sendSize = p.b.size() + sizeof(qint32); + + d->sendingPackets.append(sendSize); + qint32 sendSize32 = sendSize; + qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); + Q_ASSERT(writeBytes == sizeof(qint32)); + writeBytes = d->dev->write(p.b); + Q_ASSERT(writeBytes == p.b.size()); +} + +/*! + Returns the number of received packets yet to be read. + */ +qint64 QPacketProtocol::packetsAvailable() const +{ + return d->packets.count(); +} + +/*! + Discard any unread packets. + */ +void QPacketProtocol::clear() +{ + d->packets.clear(); +} + +/*! + Return the next unread packet, or an invalid QPacket instance if no packets + are available. This method does NOT block. + */ +QPacket QPacketProtocol::read() +{ + if(0 == d->packets.count()) + return QPacket(); + + QPacket rv(d->packets.at(0)); + d->packets.removeFirst(); + return rv; +} + +/*! + Return the QIODevice passed to the QPacketProtocol constructor. +*/ +QIODevice * QPacketProtocol::device() +{ + return d->dev; +} + +/*! + \fn void QPacketProtocol::readyRead() + + Emitted whenever a new packet is received. Applications may use + QPacketProtocol::read() to retrieve this packet. + */ + +/*! + \fn void QPacketProtocol::invalidPacket() + + A packet larger than the maximum allowable packet size was received. The + packet will be discarded and, as it indicates corruption in the protocol, no + further packets will be received. + */ + +/*! + \fn void QPacketProtocol::packetWritten() + + Emitted each time a packet is completing written to the device. This signal + may be used for communications flow control. + */ + +/*! + \class QPacket + \inpublicgroup QtBaseModule + + \brief The QPacket class encapsulates an unfragmentable packet of data to be + transmitted by QPacketProtocol. + + The QPacket class works together with QPacketProtocol to make it simple to + send arbitrary sized data "packets" across fragmented transports such as TCP + and UDP. + + QPacket provides a QDataStream interface to an unfragmentable packet. + Applications should construct a QPacket, propagate it with data and then + transmit it over a QPacketProtocol instance. For example: + \code + QPacketProtocol protocol(...); + + QPacket myPacket; + myPacket << "Hello world!" << 123; + protocol.send(myPacket); + \endcode + + As long as both ends of the connection are using the QPacketProtocol class, + the data within this packet will be delivered unfragmented at the other end, + ready for extraction. + + \code + QByteArray greeting; + int count; + + QPacket myPacket = protocol.read(); + + myPacket >> greeting >> count; + \endcode + + Only packets returned from QPacketProtocol::read() may be read from. QPacket + instances constructed by directly by applications are for transmission only + and are considered "write only". Attempting to read data from them will + result in undefined behaviour. + + \ingroup io + \sa QPacketProtocol + */ + +/*! + Constructs an empty write-only packet. + */ +QPacket::QPacket() +: QDataStream(), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::WriteOnly); + setDevice(buf); +} + +/*! + Destroys the QPacket instance. + */ +QPacket::~QPacket() +{ + if(buf) { + delete buf; + buf = 0; + } +} + +/*! + Creates a copy of \a other. The initial stream positions are shared, but the + two packets are otherwise independant. + */ +QPacket::QPacket(const QPacket & other) +: QDataStream(), b(other.b), buf(0) +{ + buf = new QBuffer(&b); + buf->open(other.buf->openMode()); + setDevice(buf); +} + +/*! + \internal + */ +QPacket::QPacket(const QByteArray & ba) +: QDataStream(), b(ba), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::ReadOnly); + setDevice(buf); +} + +/*! + Returns true if this packet is empty - that is, contains no data. + */ +bool QPacket::isEmpty() const +{ + return b.isEmpty(); +} + +/*! + Clears data in the packet. This is useful for reusing one writable packet. + For example + \code + QPacketProtocol protocol(...); + + QPacket packet; + + packet << "Hello world!" << 123; + protocol.send(packet); + + packet.clear(); + packet << "Goodbyte world!" << 789; + protocol.send(packet); + \endcode + */ +void QPacket::clear() +{ + QBuffer::OpenMode oldMode = buf->openMode(); + buf->close(); + b.clear(); + buf->setBuffer(&b); // reset QBuffer internals with new size of b. + buf->open(oldMode); +} + +/*! + \class QPacketAutoSend + \inpublicgroup QtBaseModule + + \internal + */ +QPacketAutoSend::QPacketAutoSend(QPacketProtocol * _p) +: QPacket(), p(_p) +{ +} + +QPacketAutoSend::~QPacketAutoSend() +{ + if(!b.isEmpty()) + p->send(*this); +} + +#include "qpacketprotocol.moc" + diff --git a/src/declarative/debugger/qpacketprotocol.h b/src/declarative/debugger/qpacketprotocol.h new file mode 100644 index 0000000..6dd8bda --- /dev/null +++ b/src/declarative/debugger/qpacketprotocol.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QPACKETPROTOCOL_H +#define QPACKETPROTOCOL_H + +#include <QtCore/qobject.h> +#include <QtCore/qdatastream.h> + +class QIODevice; +class QBuffer; +class QPacket; +class QPacketAutoSend; +class QPacketProtocolPrivate; + +class Q_DECLARATIVE_EXPORT QPacketProtocol : public QObject +{ +Q_OBJECT +public: + explicit QPacketProtocol(QIODevice * dev, QObject * parent = 0); + virtual ~QPacketProtocol(); + + qint32 maximumPacketSize() const; + qint32 setMaximumPacketSize(qint32); + + QPacketAutoSend send(); + void send(const QPacket &); + + qint64 packetsAvailable() const; + QPacket read(); + + void clear(); + + QIODevice * device(); + +signals: + void readyRead(); + void invalidPacket(); + void packetWritten(); + +private: + QPacketProtocolPrivate * d; +}; + + +class Q_DECLARATIVE_EXPORT QPacket : public QDataStream +{ +public: + QPacket(); + QPacket(const QPacket &); + virtual ~QPacket(); + + void clear(); + bool isEmpty() const; + +protected: + friend class QPacketProtocol; + QPacket(const QByteArray & ba); + QByteArray b; + QBuffer * buf; +}; + +class Q_DECLARATIVE_EXPORT QPacketAutoSend : public QPacket +{ +public: + virtual ~QPacketAutoSend(); + +private: + friend class QPacketProtocol; + QPacketAutoSend(QPacketProtocol *); + QPacketProtocol * p; +}; + +#endif diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp index cac5d59..c60a379 100644 --- a/src/declarative/fx/qfxvisualitemmodel.cpp +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -235,7 +235,7 @@ QFxVisualItemModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro if (values.isEmpty()) return QVariant(); else - return *values.begin(); + return values.value(iter.key()); } } } else if (data->m_model->m_abstractItemModel) { @@ -644,7 +644,7 @@ void QFxVisualItemModel::_q_itemsChanged(int index, int count, int role = data->role(prop); if (roles.contains(role)) { if (d->m_listModelInterface) { - data->setValue(prop, *d->m_listModelInterface->data(ii, QList<int>() << role).begin()); + data->setValue(prop, d->m_listModelInterface->data(ii, QList<int>() << role).value(role)); } else if (d->m_abstractItemModel) { QModelIndex index = d->m_abstractItemModel->index(ii, 0); data->setValue(prop, d->m_abstractItemModel->data(index, role)); diff --git a/src/declarative/qml/parser/javascript.g b/src/declarative/qml/parser/javascript.g index 155630b..8cabeea 100644 --- a/src/declarative/qml/parser/javascript.g +++ b/src/declarative/qml/parser/javascript.g @@ -875,7 +875,7 @@ case $rule_number: { PrimaryExpression: T_NUMERIC_LITERAL ; /. case $rule_number: { - AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval, lexer->flags); node->literalToken = loc(1); sym(1).Node = node; } break; @@ -2830,7 +2830,7 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ; yytoken = *tk; yylval = 0; yylloc = token_buffer[0].loc; - yylloc.length = 0; + yylloc.length = 0; first_token = &token_buffer[0]; last_token = &token_buffer[2]; @@ -2852,7 +2852,7 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ; yytoken = tk; yylval = 0; yylloc = token_buffer[0].loc; - yylloc.length = 0; + yylloc.length = 0; action = errorState; goto _Lcheck_token; diff --git a/src/declarative/qml/parser/javascriptast.cpp b/src/declarative/qml/parser/javascriptast.cpp index 083dd28..130229b 100644 --- a/src/declarative/qml/parser/javascriptast.cpp +++ b/src/declarative/qml/parser/javascriptast.cpp @@ -49,6 +49,44 @@ QT_BEGIN_NAMESPACE namespace JavaScript { namespace AST { +int NumericLiteral::suffixLength[] = { + 0, // noSuffix + 2, // emSuffix + 2, // exSuffix + 2, // pxSuffix + 2, // cmSuffix + 2, // mmSuffix + 2, // inSuffix + 2, // ptSuffix + 2, // pcSuffix + 3, // degSuffix + 3, // radSuffix + 4, // gradSuffix + 2, // msSuffix + 1, // sSuffix + 2, // hzSuffix + 3 // khzSuffix +}; + +const char *const NumericLiteral::suffixSpell[] = { + "", + "em", + "ex", + "px", + "cm", + "mm", + "in", + "pt", + "pc", + "deg", + "rad", + "grad", + "ms", + "s", + "hz", + "khz" +}; + ExpressionNode *Node::expressionCast() { return 0; diff --git a/src/declarative/qml/parser/javascriptast_p.h b/src/declarative/qml/parser/javascriptast_p.h index 134f3cc..b5fd922 100644 --- a/src/declarative/qml/parser/javascriptast_p.h +++ b/src/declarative/qml/parser/javascriptast_p.h @@ -398,8 +398,30 @@ class NumericLiteral: public ExpressionNode public: JAVASCRIPT_DECLARE_AST_NODE(NumericLiteral) - NumericLiteral(double v): - value (v) { kind = K; } + enum Suffix { // ### keep it in sync with the Suffix enum in javascriptlexer_p.h + noSuffix, + emSuffix, + exSuffix, + pxSuffix, + cmSuffix, + mmSuffix, + inSuffix, + ptSuffix, + pcSuffix, + degSuffix, + radSuffix, + gradSuffix, + msSuffix, + sSuffix, + hzSuffix, + khzSuffix + }; + + static int suffixLength[]; + static const char *const suffixSpell[]; + + NumericLiteral(double v, int suffix): + value(v), suffix(suffix) { kind = K; } virtual ~NumericLiteral() {} virtual void accept0(Visitor *visitor); @@ -412,6 +434,7 @@ public: // attributes: double value; + int suffix; SourceLocation literalToken; }; @@ -2314,7 +2337,7 @@ public: virtual SourceLocation firstSourceLocation() const { if (defaultToken.isValid()) - return defaultToken; + return defaultToken; return propertyToken; } @@ -2375,9 +2398,9 @@ public: virtual SourceLocation firstSourceLocation() const { if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) - return funDecl->firstSourceLocation(); + return funDecl->firstSourceLocation(); else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) - return varStmt->firstSourceLocation(); + return varStmt->firstSourceLocation(); return SourceLocation(); } @@ -2385,9 +2408,9 @@ public: virtual SourceLocation lastSourceLocation() const { if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) - return funDecl->lastSourceLocation(); + return funDecl->lastSourceLocation(); else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) - return varStmt->lastSourceLocation(); + return varStmt->lastSourceLocation(); return SourceLocation(); } diff --git a/src/declarative/qml/parser/javascriptlexer.cpp b/src/declarative/qml/parser/javascriptlexer.cpp index 7455b87..fda6ad2 100644 --- a/src/declarative/qml/parser/javascriptlexer.cpp +++ b/src/declarative/qml/parser/javascriptlexer.cpp @@ -377,7 +377,7 @@ int Lexer::findReservedWord(const QChar *c, int size) const else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('p') && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('r') - && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y')) + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y')) return JavaScriptGrammar::T_PROPERTY; else if (check_reserved) { if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b') @@ -753,12 +753,65 @@ int Lexer::lex() bol = false; } + if (state == Number) { + // CSS-style suffix for numeric literals + + flags = noSuffix; + + if (current == 'e' && next1 == 'm') { + flags = emSuffix; + shift(2); + } else if (current == 'e' && next1 == 'x') { + flags = exSuffix; + shift(2); + } else if (current == 'p' && next1 == 'x') { + flags = pxSuffix; + shift(2); + } else if (current == 'c' && next1 == 'm') { + flags = cmSuffix; + shift(2); + } else if (current == 'm' && next1 == 'm') { + flags = mmSuffix; + shift(2); + } else if (current == 'i' && next1 == 'n') { + flags = inSuffix; + shift(2); + } else if (current == 'p' && next1 == 't') { + flags = ptSuffix; + shift(2); + } else if (current == 'p' && next1 == 'c') { + flags = pcSuffix; + shift(1); + } else if (current == 'd' && next1 == 'e' && next2 == 'g') { + flags = degSuffix; + shift(3); + } else if (current == 'r' && next1 == 'a' && next2 == 'd') { + flags = radSuffix; + shift(3); + } else if (current == 'g' && next1 == 'r' && next2 == 'a' && next3 == 'd') { + flags = gradSuffix; + shift(4); + } else if (current == 'm' && next1 == 's') { + flags = msSuffix; + shift(2); + } else if (current == 's') { + flags = sSuffix; + shift(1); + } else if (current == 'h' && next1 == 'z') { + flags = hzSuffix; + shift(2); + } else if (current == 'k' && next1 == 'h' && next2 == 'z') { + flags = khzSuffix; + shift(3); + } + } + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad if ((state == Number || state == Octal || state == Hex) && isIdentLetter(current)) { state = Bad; err = IllegalIdentifier; - errmsg = QLatin1String("Identifier cannot start with numeric literal"); + errmsg = QLatin1String("Identifier cannot start with numeric literal `%1'"); } // terminate string diff --git a/src/declarative/qml/parser/javascriptlexer_p.h b/src/declarative/qml/parser/javascriptlexer_p.h index 092609c..a47c1ae 100644 --- a/src/declarative/qml/parser/javascriptlexer_p.h +++ b/src/declarative/qml/parser/javascriptlexer_p.h @@ -112,6 +112,25 @@ public: Other, Bad }; + enum Suffix { + noSuffix, + emSuffix, + exSuffix, + pxSuffix, + cmSuffix, + mmSuffix, + inSuffix, + ptSuffix, + pcSuffix, + degSuffix, + radSuffix, + gradSuffix, + msSuffix, + sSuffix, + hzSuffix, + khzSuffix + }; + enum Error { NoError, IllegalCharacter, diff --git a/src/declarative/qml/parser/javascriptparser.cpp b/src/declarative/qml/parser/javascriptparser.cpp index 7ff438e..34ecd0e 100644 --- a/src/declarative/qml/parser/javascriptparser.cpp +++ b/src/declarative/qml/parser/javascriptparser.cpp @@ -430,7 +430,7 @@ case 51: { } break; case 52: { - AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval, lexer->flags); node->literalToken = loc(1); sym(1).Node = node; } break; @@ -1675,7 +1675,7 @@ case 320: { yytoken = *tk; yylval = 0; yylloc = token_buffer[0].loc; - yylloc.length = 0; + yylloc.length = 0; first_token = &token_buffer[0]; last_token = &token_buffer[2]; @@ -1697,7 +1697,7 @@ case 320: { yytoken = tk; yylval = 0; yylloc = token_buffer[0].loc; - yylloc.length = 0; + yylloc.length = 0; action = errorState; goto _Lcheck_token; diff --git a/src/declarative/qml/parser/parser.pri b/src/declarative/qml/parser/parser.pri index b4d226a..72bd46c 100644 --- a/src/declarative/qml/parser/parser.pri +++ b/src/declarative/qml/parser/parser.pri @@ -1,4 +1,6 @@ +INCLUDEPATH += $$PWD + HEADERS += $$PWD/javascriptast_p.h \ $$PWD/javascriptastfwd_p.h \ $$PWD/javascriptastvisitor_p.h \ diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 99e30e4..b6ac30e 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -22,7 +22,8 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlparserstatus.cpp \ qml/qmlcompositetypemanager.cpp \ qml/qmlinfo.cpp \ - qml/qmlerror.cpp + qml/qmlerror.cpp \ + qml/qmlscriptparser.cpp HEADERS += qml/qmlparser_p.h \ qml/qmlinstruction_p.h \ @@ -59,15 +60,13 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlcompositetypemanager_p.h \ qml/qmllist.h \ qml/qmldeclarativedata_p.h \ - qml/qmlerror.h + qml/qmlerror.h \ + qml/qmlscriptparser_p.h # for qtscript debugger QT += scripttools -include(script/script.pri) -# new language front-end +include(script/script.pri) include(parser/parser.pri) +include(rewriter/rewriter.pri) -HEADERS += qml/qmlscriptparser_p.h - -SOURCES += qml/qmlscriptparser.cpp diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 5f92721..89a1774 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -154,28 +154,18 @@ int QmlCompiledData::indexForLocation(const QmlParser::LocationSpan &l) } QmlCompiler::QmlCompiler() -: exceptionLine(-1), exceptionColumn(-1), output(0) +: output(0) { } bool QmlCompiler::isError() const { - return exceptionLine != -1; + return !exceptions.isEmpty(); } QList<QmlError> QmlCompiler::errors() const { - QList<QmlError> rv; - - if(isError()) { - QmlError error; - error.setDescription(exceptionDescription); - error.setLine(exceptionLine); - error.setColumn(exceptionColumn); - rv << error; - } - - return rv; + return exceptions; } bool QmlCompiler::isValidId(const QString &val) @@ -436,21 +426,29 @@ void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) #define COMPILE_EXCEPTION2(token, desc) \ { \ - exceptionLine = token->location.start.line; \ - exceptionColumn = token->location.start.column; \ + QString exceptionDescription; \ + QmlError error; \ + error.setUrl(output->url); \ + error.setLine(token->location.start.line); \ + error.setColumn(token->location.start.column); \ QDebug d(&exceptionDescription); \ d << desc; \ - exceptionDescription = exceptionDescription.trimmed(); \ + error.setDescription(exceptionDescription.trimmed()); \ + exceptions << error; \ return false; \ } #define COMPILE_EXCEPTION(desc) \ { \ - exceptionLine = obj->location.start.line; \ - exceptionColumn = obj->location.start.column; \ + QString exceptionDescription; \ + QmlError error; \ + error.setUrl(output->url); \ + error.setLine(obj->location.start.line); \ + error.setColumn(obj->location.start.column); \ QDebug d(&exceptionDescription); \ d << desc; \ - exceptionDescription = exceptionDescription.trimmed(); \ + error.setDescription(exceptionDescription.trimmed()); \ + exceptions << error; \ return false; \ } @@ -466,7 +464,7 @@ bool QmlCompiler::compile(QmlEngine *engine, #ifdef Q_ENABLE_PERFORMANCE_LOG QFxPerfTimer<QFxPerf::Compile> pc; #endif - exceptionLine = -1; + exceptions.clear(); Q_ASSERT(out); reset(out, true); @@ -481,6 +479,17 @@ bool QmlCompiler::compile(QmlEngine *engine, ref.type = tref.type; else if (tref.unit) { ref.component = tref.unit->toComponent(engine); + + if (ref.component->isError()) { + QmlError error; + error.setUrl(output->url); + error.setDescription(QLatin1String("Unable to create type ") + + unit->data.types().at(ii)); + exceptions << error; + exceptions << ref.component->errors(); + reset(out, true); + return false; + } ref.ref = tref.unit; ref.ref->addref(); } @@ -552,23 +561,28 @@ bool QmlCompiler::compileObject(Object *obj, int ctxt) COMPILE_CHECK(compileDynamicMeta(obj)); + int parserStatusCast = -1; if (obj->type != -1) { - if (output->types.at(obj->type).component) { - QmlInstruction begin; - begin.type = QmlInstruction::TryBeginObject; - begin.line = obj->location.start.line; - output->bytecode << begin; - } else { - int cast = QmlMetaType::qmlParserStatusCast(QmlMetaType::type(output->types.at(obj->type).className)); - if (cast != -1) { - QmlInstruction begin; - begin.type = QmlInstruction::BeginObject; - begin.begin.castValue = cast; - begin.line = obj->location.start.line; - output->bytecode << begin; - } + // ### Optimize + const QMetaObject *mo = obj->metatype; + QmlType *type = 0; + while (!type && mo) { + type = QmlMetaType::qmlType(mo); + mo = mo->superClass(); } - } + + Q_ASSERT(type); + + parserStatusCast = type->parserStatusCast(); + } + + if (parserStatusCast != -1) { + QmlInstruction begin; + begin.type = QmlInstruction::BeginObject; + begin.begin.castValue = parserStatusCast; + begin.line = obj->location.start.line; + output->bytecode << begin; + } bool isCustomParser = output->types.at(obj->type).type && output->types.at(obj->type).type->customParser() != 0; @@ -611,22 +625,12 @@ bool QmlCompiler::compileObject(Object *obj, int ctxt) output->indexForByteArray(customData); } - if (obj->type != -1) { - if (output->types.at(obj->type).component) { - QmlInstruction complete; - complete.type = QmlInstruction::TryCompleteObject; - complete.line = obj->location.start.line; - output->bytecode << complete; - } else { - int cast = QmlMetaType::qmlParserStatusCast(QmlMetaType::type(output->types.at(obj->type).className)); - if (cast != -1) { - QmlInstruction complete; - complete.type = QmlInstruction::CompleteObject; - complete.complete.castValue = cast; - complete.line = obj->location.start.line; - output->bytecode << complete; - } - } + if (parserStatusCast != -1) { + QmlInstruction complete; + complete.type = QmlInstruction::CompleteObject; + complete.complete.castValue = parserStatusCast; + complete.line = obj->location.start.line; + output->bytecode << complete; } return true; @@ -671,6 +675,9 @@ bool QmlCompiler::compileComponent(Object *obj, int ctxt) id.line = idProp->location.start.line; id.setId.value = pref; id.setId.save = -1; + + savedTypes.insert(output->bytecode.count(), -1); + output->bytecode << id; } @@ -932,6 +939,7 @@ bool QmlCompiler::compileIdProperty(QmlParser::Property *prop, id.line = prop->values.at(0)->location.start.line; id.setId.value = pref; id.setId.save = -1; + savedTypes.insert(output->bytecode.count(), obj->type); output->bytecode << id; obj->id = val.toLatin1(); @@ -972,16 +980,16 @@ bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, if (prop->type != 0) prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); + if (prop->index == -1) + COMPILE_EXCEPTION2(prop, "Cannot access non-existant property" << prop->name); + + if (!QmlMetaType::isObject(prop->value->metatype)) + COMPILE_EXCEPTION2(prop, "Cannot nest non-QObject property" << prop->name); + QmlInstruction fetch; - if (prop->index != -1 && - QmlMetaType::isObject(prop->value->metatype)) { - fetch.type = QmlInstruction::FetchObject; - fetch.fetch.property = prop->index; - fetch.fetch.isObject = true; - } else { - fetch.type = QmlInstruction::ResolveFetchObject; - fetch.fetch.property = output->indexForByteArray(prop->name); - } + fetch.type = QmlInstruction::FetchObject; + fetch.fetch.property = prop->index; + fetch.fetch.isObject = true; fetch.line = prop->location.start.line; output->bytecode << fetch; @@ -1098,8 +1106,40 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, { if (v->object->type != -1) v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); + + if (prop->index == -1) + COMPILE_EXCEPTION2(prop, "Cannot assign object to non-existant property" << prop->name); + + + if (QmlMetaType::isInterface(prop->type)) { + + COMPILE_CHECK(compileObject(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::StoreInterface; + assign.line = v->object->location.start.line; + assign.storeObject.propertyIndex = prop->index; + assign.storeObject.cast = 0; + output->bytecode << assign; + + v->type = Value::CreatedObject; + + } else if (prop->type == -1) { + + // Variant + // ### Is it always? + COMPILE_CHECK(compileObject(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::StoreVariantObject; + assign.line = v->object->location.start.line; + assign.storeObject.propertyIndex = prop->index; + assign.storeObject.cast = 0; + output->bytecode << assign; - if (v->object->metaObject()) { + v->type = Value::CreatedObject; + } else { const QMetaObject *propmo = QmlMetaType::rawMetaObjectForType(prop->type); @@ -1124,22 +1164,7 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, } } - if (!propmo && !isPropertyValue) { - COMPILE_CHECK(compileObject(v->object, ctxt)); - - QmlInstruction assign; - assign.type = QmlInstruction::AssignObject; - assign.line = v->object->location.start.line; - assign.assignObject.castValue = 0; - if (prop->isDefault) - assign.assignObject.property = -1; - else - assign.assignObject.property = - output->indexForByteArray(prop->name); - output->bytecode << assign; - - v->type = Value::CreatedObject; - } else if (isAssignable) { + if (isAssignable) { COMPILE_CHECK(compileObject(v->object, ctxt)); QmlInstruction assign; @@ -1167,36 +1192,16 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, } else if (isPropertyValue) { COMPILE_CHECK(compileObject(v->object, ctxt)); - if (prop->index != -1) { - QmlInstruction assign; - assign.type = QmlInstruction::StoreValueSource; - assign.line = v->object->location.start.line; - assign.assignValueSource.property = prop->index; - output->bytecode << assign; - } else { - QmlInstruction assign; - assign.type = QmlInstruction::AssignValueSource; - assign.line = v->object->location.start.line; - assign.assignValueSource.property = output->indexForByteArray(prop->name);; - output->bytecode << assign; - } + QmlInstruction assign; + assign.type = QmlInstruction::StoreValueSource; + assign.line = v->object->location.start.line; + assign.assignValueSource.property = prop->index; + output->bytecode << assign; v->type = Value::ValueSource; } else { - COMPILE_EXCEPTION("Unassignable object"); + COMPILE_EXCEPTION2(v->object, "Unassignable object"); } - - } else { - COMPILE_CHECK(compileObject(v->object, ctxt)); - - QmlInstruction assign; - assign.type = QmlInstruction::AssignObject; - assign.line = v->object->location.start.line; - assign.assignObject.property = output->indexForByteArray(prop->name); - assign.assignObject.castValue = 0; - output->bytecode << assign; - - v->type = Value::CreatedObject; } return true; @@ -1368,6 +1373,7 @@ bool QmlCompiler::compileBinding(const QString &bind, QmlParser::Property *prop, assign.assignBinding.category = QmlMetaProperty::propertyCategory(mp); } + savedTypes.insert(output->bytecode.count(), prop->type); output->bytecode << assign; } else { @@ -1410,8 +1416,7 @@ int QmlCompiler::optimizeExpressions(int start, int end, int patch) if (instr.type == QmlInstruction::StoreBinding || instr.type == QmlInstruction::StoreCompiledBinding) { ++bindingsCount; - } else if (instr.type == QmlInstruction::TryBeginObject || - instr.type == QmlInstruction::BeginObject) { + } else if (instr.type == QmlInstruction::BeginObject) { ++parserStatusCount; } @@ -1427,6 +1432,25 @@ int QmlCompiler::optimizeExpressions(int start, int end, int patch) if (ids.contains(slt) && instr.assignBinding.category == QmlMetaProperty::Object) { int id = ids[slt]; + + int idType = savedTypes.value(id); + int storeType = savedTypes.value(ii); + + const QMetaObject *idMo = (idType == -1)?&QmlComponent::staticMetaObject:output->types.at(idType).metaObject(); + const QMetaObject *storeMo = + QmlMetaType::rawMetaObjectForType(storeType); + + bool canAssign = false; + while (!canAssign && idMo) { + if (idMo == storeMo) + canAssign = true; + else + idMo = idMo->superClass(); + } + + if (!canAssign) + continue; + int saveId = -1; if (output->bytecode.at(id).setId.save != -1) { @@ -1444,7 +1468,7 @@ int QmlCompiler::optimizeExpressions(int start, int end, int patch) rwinstr.pushProperty.property = prop; QmlInstruction instr; - instr.type = QmlInstruction::AssignStackObject; + instr.type = QmlInstruction::StoreStackObject; instr.line = 0; instr.assignStackObject.property = newInstrs; instr.assignStackObject.object = saveId; diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index bc04cfa..3280866 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -170,10 +170,11 @@ private: int optimizeExpressions(int start, int end, int patch = -1); QSet<QString> ids; - qint64 exceptionLine; - qint64 exceptionColumn; - QString exceptionDescription; + + QList<QmlError> exceptions; QmlCompiledData *output; + + QHash<int, int> savedTypes; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp index 0da1a92..a5e302c 100644 --- a/src/declarative/qml/qmlcompositetypemanager.cpp +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -86,6 +86,8 @@ QmlComponent *QmlCompositeTypeData::toComponent(QmlEngine *engine) component = new QmlComponent(engine, cc, -1, -1, 0); } else { component = new QmlComponent(engine, 0); + component->d_func()->url = QUrl(url); + component->d_func()->errors = errors; } } diff --git a/src/declarative/qml/qmldom.cpp b/src/declarative/qml/qmldom.cpp index 9871fcb..b689ec5 100644 --- a/src/declarative/qml/qmldom.cpp +++ b/src/declarative/qml/qmldom.cpp @@ -44,13 +44,15 @@ #include "private/qmlcompiler_p.h" #include "private/qmlengine_p.h" #include "qmlcompiledcomponent_p.h" -#include <QtCore/qbytearray.h> -#include <QtCore/qstring.h> - +#include <QtCore/QByteArray> +#include <QtCore/QDebug> +#include <QtCore/QString> #include "qmlscriptparser_p.h" QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP) + QmlDomDocumentPrivate::QmlDomDocumentPrivate() : root(0) { @@ -193,7 +195,11 @@ bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data, const QUrl } if (td->data.tree()) { - td->data.tree()->dump(); + if (compilerDump()) { + qWarning() << "-AST------------------------------------------------------------------------------"; + td->data.tree()->dump(); + qWarning() << "----------------------------------------------------------------------------------"; + } d->root = td->data.tree(); d->root->addref(); } diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index 923d36f..bb40063 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -127,9 +127,6 @@ void QmlCompiledComponent::dump(QmlInstruction *instr, int idx) case QmlInstruction::AssignSignalObject: qWarning() << idx << "\t" << line << "\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal); break; - case QmlInstruction::AssignValueSource: - qWarning() << idx << "\t" << line << "\t" << "ASSIGN_VALUE_SOURCE\t" << instr->assignValueSource.property << "\t\t\t" << datas.at(instr->assignValueSource.property); - break; case QmlInstruction::StoreBinding: qWarning() << idx << "\t" << line << "\t" << "STORE_BINDING\t\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context << primitives.at(instr->assignBinding.value); break; @@ -139,21 +136,12 @@ void QmlCompiledComponent::dump(QmlInstruction *instr, int idx) case QmlInstruction::StoreValueSource: qWarning() << idx << "\t" << line << "\t" << "STORE_VALUE_SOURCE\t" << instr->assignValueSource.property; break; - case QmlInstruction::TryBeginObject: - qWarning() << idx << "\t" << line << "\t" << "TRY_BEGIN"; - break; case QmlInstruction::BeginObject: qWarning() << idx << "\t" << line << "\t" << "BEGIN\t\t\t" << instr->begin.castValue; break; - case QmlInstruction::TryCompleteObject: - qWarning() << idx << "\t" << line << "\t" << "TRY_COMPLETE"; - break; case QmlInstruction::CompleteObject: qWarning() << idx << "\t" << line << "\t" << "COMPLETE\t\t" << instr->complete.castValue; break; - case QmlInstruction::AssignObject: - qWarning() << idx << "\t" << line << "\t" << "ASSIGN_OBJECT\t\t" << instr->assignObject.property << "\t" << instr->assignObject.castValue << "\t\t" << ((instr->assignObject.property == -1)?QByteArray("default"):datas.at(instr->assignObject.property)); - break; case QmlInstruction::AssignObjectList: qWarning() << idx << "\t" << line << "\t" << "ASSIGN_OBJECT_LIST\t" << instr->assignObject.property << "\t" << instr->assignObject.castValue << "\t\t" << ((instr->assignObject.property == -1)?QByteArray("default"):datas.at(instr->assignObject.property)); break; @@ -169,24 +157,15 @@ void QmlCompiledComponent::dump(QmlInstruction *instr, int idx) case QmlInstruction::FetchObject: qWarning() << idx << "\t" << line << "\t" << "FETCH\t\t\t" << instr->fetch.property; break; - case QmlInstruction::ResolveFetchObject: - qWarning() << idx << "\t" << line << "\t" << "RESOLVE_FETCH\t\t" << instr->fetch.property << "\t\t\t" << datas.at(instr->fetch.property); - break; case QmlInstruction::PopFetchedObject: qWarning() << idx << "\t" << line << "\t" << "POP"; break; case QmlInstruction::PopQList: qWarning() << idx << "\t" << line << "\t" << "POP_QLIST"; break; - case QmlInstruction::NoOp: - qWarning() << idx << "\t" << line << "\t" << "NOOP"; - break; case QmlInstruction::PushProperty: qWarning() << idx << "\t" << line << "\t" << "PUSH_PROPERTY" << "\t\t" << instr->pushProperty.property; break; - case QmlInstruction::AssignStackObject: - qWarning() << idx << "\t" << line << "\t" << "ASSIGN_STACK_OBJ" << "\t" << instr->assignStackObject.property << "\t" << instr->assignStackObject.object; - break; case QmlInstruction::StoreStackObject: qWarning() << idx << "\t" << line << "\t" << "STORE_STACK_OBJ" << "\t" << instr->assignStackObject.property << "\t" << instr->assignStackObject.object; break; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index a21ccee..abefd6c 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -83,7 +83,6 @@ public: // StoreObject - Pop the object on the top of the object stack and // store it in a core property StoreReal, /* storeReal */ - StoreInstructionsStart = StoreReal, StoreInteger, /* storeInteger */ StoreBool, /* storeBool */ StoreString, /* storeString */ @@ -99,7 +98,8 @@ public: StoreRectF, /* storeRect */ StoreVariant, /* storeString */ StoreObject, /* storeObject */ - StoreInstructionsEnd = StoreObject, + StoreVariantObject, /* storeObject */ + StoreInterface, /* storeObject */ StoreSignal, /* storeSignal */ @@ -113,24 +113,19 @@ public: AssignSignalObject, /* assignSignalObject */ AssignCustomType, /* assignCustomType */ - AssignValueSource, /* assignValueSource */ StoreBinding, /* assignBinding */ StoreCompiledBinding, /* assignBinding */ StoreValueSource, /* assignValueSource */ - TryBeginObject, BeginObject, /* begin */ - TryCompleteObject, CompleteObject, /* complete */ - AssignObject, /* assignObject */ AssignObjectList, /* assignObject */ FetchAttached, /* fetchAttached */ FetchQmlList, /* fetchQmlList */ FetchQList, /* fetch */ FetchObject, /* fetch */ - ResolveFetchObject, /* fetch */ // // Stack manipulation @@ -144,21 +139,12 @@ public: // Expression optimizations // // PushProperty - Save the property for later use - // AssignStackObject - Assign the stack object // StoreStackObject - Assign the stack object (no checks) PushProperty, /* pushProperty */ - AssignStackObject, /* assignStackObject */ - StoreStackObject, /* assignStackObject */ - - - // - // Miscellaneous - // - // NoOp - Do nothing - NoOp + StoreStackObject /* assignStackObject */ }; QmlInstruction() - : type(NoOp), line(0) {} + : line(0) {} Type type; unsigned short line; diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index 5b3564f..fde2771 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -9,6 +9,8 @@ #include "parser/javascriptastvisitor_p.h" #include "parser/javascriptast_p.h" +#include "rewriter/textwriter_p.h" + #include <QStack> #include <QCoreApplication> #include <QtDebug> @@ -22,6 +24,46 @@ using namespace QmlParser; namespace { +class RewriteNumericLiterals: protected AST::Visitor +{ + unsigned _position; + TextWriter _writer; + +public: + QString operator()(QString code, unsigned position, AST::Node *node) + { + _position = position; + + AST::Node::acceptChild(node, this); + + _writer.write(&code); + + return code; + } + +protected: + using AST::Visitor::visit; + + virtual bool visit(AST::NumericLiteral *node) + { + if (node->suffix != AST::NumericLiteral::noSuffix) { + const int suffixLength = AST::NumericLiteral::suffixLength[node->suffix]; + const char *suffixSpell = AST::NumericLiteral::suffixSpell[node->suffix]; + QString pre; + pre += QLatin1String("qmlNumberFrom"); + pre += QChar(QLatin1Char(suffixSpell[0])).toUpper(); + pre += QLatin1String(&suffixSpell[1]); + pre += QLatin1Char('('); + _writer.replace(node->literalToken.begin() - _position, 0, pre); + _writer.replace(node->literalToken.end() - _position - suffixLength, + suffixLength, + QLatin1String(")")); + } + + return false; + } +}; + class ProcessAST: protected AST::Visitor { struct State { @@ -107,24 +149,30 @@ protected: QString textAt(const AST::SourceLocation &loc) const { return _contents.mid(loc.offset, loc.length); } + QString textAt(const AST::SourceLocation &first, const AST::SourceLocation &last) const { return _contents.mid(first.offset, last.offset + last.length - first.offset); } - QString asString(AST::ExpressionNode *expr) const + RewriteNumericLiterals rewriteNumericLiterals; + + QString asString(AST::ExpressionNode *expr) { if (! expr) return QString(); - return textAt(expr->firstSourceLocation(), expr->lastSourceLocation()); + return rewriteNumericLiterals(textAt(expr->firstSourceLocation(), expr->lastSourceLocation()), + expr->firstSourceLocation().offset, expr); } - QString asString(AST::Statement *stmt) const + QString asString(AST::Statement *stmt) { if (! stmt) return QString(); - QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()); + QString s = rewriteNumericLiterals(textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()), + stmt->firstSourceLocation().offset, stmt); + s += QLatin1Char('\n'); return s; } @@ -214,7 +262,7 @@ ProcessAST::defineObjectBinding_helper(AST::UiQualifiedId *propertyName, int propertyCount = 0; for (; propertyName; propertyName = propertyName->next){ ++propertyCount; - _stateStack.pushProperty(propertyName->name->asString(), + _stateStack.pushProperty(propertyName->name->asString(), this->location(propertyName)); } @@ -322,7 +370,7 @@ Object *ProcessAST::defineObjectBinding(AST::UiQualifiedId *qualifiedId, script = asString(scriptBinding->statement); } - LocationSpan l = this->location(scriptBinding->statement->firstSourceLocation(), + LocationSpan l = this->location(scriptBinding->statement->firstSourceLocation(), scriptBinding->statement->lastSourceLocation()); _stateStack.pushProperty(QLatin1String("script"), l); @@ -414,7 +462,7 @@ bool ProcessAST::visit(AST::UiPublicMember *node) { "var", Object::DynamicProperty::Variant }, { "variant", Object::DynamicProperty::Variant } }; - const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / sizeof(propTypeNameToTypes[0]); bool typeFound = false; @@ -425,7 +473,7 @@ bool ProcessAST::visit(AST::UiPublicMember *node) typeFound = true; } } - + if(!typeFound) { QmlError error; error.setDescription(QCoreApplication::translate("QmlParser","Expected property type")); @@ -494,7 +542,11 @@ QmlParser::Variant ProcessAST::getVariant(AST::ExpressionNode *expr) } else if (expr->kind == AST::Node::Kind_FalseLiteral) { return QmlParser::Variant(false); } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) { - return QmlParser::Variant(lit->value, asString(expr)); + if (lit->suffix == AST::NumericLiteral::noSuffix) + return QmlParser::Variant(lit->value, asString(expr)); + else + return QmlParser::Variant(asString(expr), QmlParser::Variant::Script); + } else { if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) { @@ -515,7 +567,7 @@ bool ProcessAST::visit(AST::UiScriptBinding *node) AST::UiQualifiedId *propertyName = node->qualifiedId; for (; propertyName; propertyName = propertyName->next){ ++propertyCount; - _stateStack.pushProperty(propertyName->name->asString(), + _stateStack.pushProperty(propertyName->name->asString(), location(propertyName)); } @@ -526,7 +578,7 @@ bool ProcessAST::visit(AST::UiScriptBinding *node) if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) { primitive = getVariant(stmt->expression); } else { // do binding - primitive = QmlParser::Variant(asString(node->statement), + primitive = QmlParser::Variant(asString(node->statement), QmlParser::Variant::Script); } @@ -550,7 +602,7 @@ bool ProcessAST::visit(AST::UiArrayBinding *node) AST::UiQualifiedId *propertyName = node->qualifiedId; for (; propertyName; propertyName = propertyName->next){ ++propertyCount; - _stateStack.pushProperty(propertyName->name->asString(), + _stateStack.pushProperty(propertyName->name->asString(), location(propertyName)); } @@ -608,7 +660,7 @@ bool ProcessAST::visit(AST::UiSourceElement *node) } Value *value = new Value; - value->location = location(node->firstSourceLocation(), + value->location = location(node->firstSourceLocation(), node->lastSourceLocation()); value->value = QmlParser::Variant(source); @@ -673,7 +725,7 @@ bool QmlScriptParser::parse(const QByteArray &data, const QUrl &url) process(code, parser.ast()); // Set the url for process errors - for(int ii = 0; ii < _errors.count(); ++ii) + for(int ii = 0; ii < _errors.count(); ++ii) _errors[ii].setUrl(url); } diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index a5cc649..991c7ad 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -88,27 +88,20 @@ Q_DECLARE_PERFORMANCE_LOG(QFxCompiler) { Q_DECLARE_PERFORMANCE_METRIC(InstrStoreSignal); Q_DECLARE_PERFORMANCE_METRIC(InstrStoreObjectQmlList); Q_DECLARE_PERFORMANCE_METRIC(InstrAssignSignalObject); - Q_DECLARE_PERFORMANCE_METRIC(InstrAssignValueSource); Q_DECLARE_PERFORMANCE_METRIC(InstrStoreBinding); Q_DECLARE_PERFORMANCE_METRIC(InstrStoreCompiledBinding); Q_DECLARE_PERFORMANCE_METRIC(InstrStoreValueSource); - Q_DECLARE_PERFORMANCE_METRIC(InstrTryBeginObject); Q_DECLARE_PERFORMANCE_METRIC(InstrBeginObject); - Q_DECLARE_PERFORMANCE_METRIC(InstrTryCompleteObject); Q_DECLARE_PERFORMANCE_METRIC(InstrCompleteObject); - Q_DECLARE_PERFORMANCE_METRIC(InstrAssignObject); Q_DECLARE_PERFORMANCE_METRIC(InstrAssignObjectList); Q_DECLARE_PERFORMANCE_METRIC(InstrFetchAttached); Q_DECLARE_PERFORMANCE_METRIC(InstrFetchQmlList); Q_DECLARE_PERFORMANCE_METRIC(InstrFetchQList); Q_DECLARE_PERFORMANCE_METRIC(InstrFetchObject); - Q_DECLARE_PERFORMANCE_METRIC(InstrResolveFetchObject); Q_DECLARE_PERFORMANCE_METRIC(InstrPopFetchedObject); Q_DECLARE_PERFORMANCE_METRIC(InstrPopQList); Q_DECLARE_PERFORMANCE_METRIC(InstrPushProperty); - Q_DECLARE_PERFORMANCE_METRIC(InstrAssignStackObject); Q_DECLARE_PERFORMANCE_METRIC(InstrStoreStackObject); - Q_DECLARE_PERFORMANCE_METRIC(InstrNoOp); Q_DECLARE_PERFORMANCE_METRIC(Dummy); } @@ -134,27 +127,20 @@ Q_DEFINE_PERFORMANCE_LOG(QFxCompiler, "QFxCompiler") { Q_DEFINE_PERFORMANCE_METRIC(InstrStoreSignal, "StoreSignal"); Q_DEFINE_PERFORMANCE_METRIC(InstrStoreObjectQmlList, "StoreObjectQmlList"); Q_DEFINE_PERFORMANCE_METRIC(InstrAssignSignalObject, "AssignSignalObject"); - Q_DEFINE_PERFORMANCE_METRIC(InstrAssignValueSource, "AssignValueSource"); Q_DEFINE_PERFORMANCE_METRIC(InstrStoreBinding, "StoreBinding"); Q_DEFINE_PERFORMANCE_METRIC(InstrStoreCompiledBinding, "StoreCompiledBinding"); Q_DEFINE_PERFORMANCE_METRIC(InstrStoreValueSource, "StoreValueSource"); - Q_DEFINE_PERFORMANCE_METRIC(InstrTryBeginObject, "TryBeginObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrBeginObject, "BeginObject"); - Q_DEFINE_PERFORMANCE_METRIC(InstrTryCompleteObject, "TryCompleteObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrCompleteObject, "CompleteObject"); - Q_DEFINE_PERFORMANCE_METRIC(InstrAssignObject, "AssignObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrAssignObjectList, "AssignObjectList"); Q_DEFINE_PERFORMANCE_METRIC(InstrFetchAttached, "FetchAttached"); Q_DEFINE_PERFORMANCE_METRIC(InstrFetchQmlList, "FetchQmlList"); Q_DEFINE_PERFORMANCE_METRIC(InstrFetchQList, "FetchQList"); Q_DEFINE_PERFORMANCE_METRIC(InstrFetchObject, "FetchObject"); - Q_DEFINE_PERFORMANCE_METRIC(InstrResolveFetchObject, "ResolveFetchObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrPopFetchedObject, "PopFetchedObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrPopQList, "PopQList"); Q_DEFINE_PERFORMANCE_METRIC(InstrPushProperty, "PushProperty"); - Q_DEFINE_PERFORMANCE_METRIC(InstrAssignStackObject, "AssignStackObject"); Q_DEFINE_PERFORMANCE_METRIC(InstrStoreStackObject, "StoreStackObject"); - Q_DEFINE_PERFORMANCE_METRIC(InstrNoOp, "NoOp"); Q_DEFINE_PERFORMANCE_METRIC(Dummy, "Dummy"); } @@ -210,6 +196,9 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in const QList<QByteArray> &datas = comp->datas; const QList<QMetaObject *> &synthesizedMetaObjects = comp->synthesizedMetaObjects;; const QList<QmlCompiledData::CustomTypeData> &customTypeData = comp->customTypeData; + const QList<int> &intData = comp->intData; + const QList<float> &floatData = comp->floatData; + #ifdef Q_ENABLE_PERFORMANCE_LOG QFxPerfTimer<QFxPerf::CompileRun> cr; @@ -231,13 +220,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in for (int ii = start; !isError() && ii < (start + count); ++ii) { QmlInstruction &instr = comp->bytecode[ii]; - if (instr.type >= QmlInstruction::StoreInstructionsStart && - instr.type <= QmlInstruction::StoreInstructionsEnd) { - - runStoreInstruction(stack, instr, comp); - - } else { - switch(instr.type) { case QmlInstruction::Init: { @@ -327,6 +309,252 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; + case QmlInstruction::StoreVariant: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreVariant> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + // XXX - can be more efficient + QVariant v = QmlStringConverters::variantFromString(primitives.at(instr.storeString.value)); + a[0] = (void *)&v; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QmlInstruction::StoreString: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreString> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&primitives.at(instr.storeString.value); + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QmlInstruction::StoreReal: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreReal> cc; +#endif + QObject *target = stack.top(); + //### moc treats qreal properties as having type double + double r = static_cast<double>(instr.storeReal.value); + void *a[1]; + a[0] = &r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeReal.propertyIndex, a); + } + break; + + case QmlInstruction::StoreBool: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreBool> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&instr.storeBool.value; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeBool.propertyIndex, a); + } + break; + + case QmlInstruction::StoreInteger: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreInteger> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&instr.storeInteger.value; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeReal.propertyIndex, a); + } + break; + + case QmlInstruction::StoreColor: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreColor> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QColor c = QColor::fromRgba(instr.storeColor.value); + a[0] = (void *)&c; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeColor.propertyIndex, a); + } + break; + + case QmlInstruction::StoreDate: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreDate> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QDate d = QDate::fromJulianDay(instr.storeDate.value); + a[0] = (void *)&d; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDate.propertyIndex, a); + } + break; + + case QmlInstruction::StoreTime: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreTime> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QTime t; + t.setHMS(intData.at(instr.storeTime.valueIndex), + intData.at(instr.storeTime.valueIndex+1), + intData.at(instr.storeTime.valueIndex+2), + intData.at(instr.storeTime.valueIndex+3)); + a[0] = (void *)&t; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeTime.propertyIndex, a); + } + break; + + case QmlInstruction::StoreDateTime: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreDateTime> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QTime t; + t.setHMS(intData.at(instr.storeDateTime.valueIndex+1), + intData.at(instr.storeDateTime.valueIndex+2), + intData.at(instr.storeDateTime.valueIndex+3), + intData.at(instr.storeDateTime.valueIndex+4)); + QDateTime dt(QDate::fromJulianDay(intData.at(instr.storeDateTime.valueIndex)), t); + a[0] = (void *)&dt; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDateTime.propertyIndex, a); + } + break; + + case QmlInstruction::StorePoint: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QPoint p = QPointF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toPoint(); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StorePointF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QPointF p(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreSize: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QSize p = QSizeF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toSize(); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreSizeF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QSizeF s(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + a[0] = (void *)&s; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreRect: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QRect r = QRectF(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)).toRect(); + a[0] = (void *)&r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QmlInstruction::StoreRectF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QRectF r(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)); + a[0] = (void *)&r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QmlInstruction::StoreObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreObject> cc; +#endif + QObject *assignObj = stack.pop(); + QObject *target = stack.top(); + + void *a[1]; + void *obj = (void *)(((char *)assignObj) + instr.storeObject.cast); + a[0] = (void *)&obj; + + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeObject.propertyIndex, a); + } + break; + + case QmlInstruction::AssignCustomType: { QObject *target = stack.top(); @@ -372,11 +600,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in QMetaObject::connect(target, prop.coreIndex(), assign, method.methodIndex()); - } else if (prop.type() & QmlMetaProperty::Property) { - instr.type = QmlInstruction::AssignObject; - instr.assignObject.castValue = 0; - instr.assignObject.property = sigIdx; - --ii; } else { VME_EXCEPTION("Cannot assign an object to signal property" << pr); } @@ -403,25 +626,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::TryBeginObject: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrTryBeginObject> cc; -#endif - QObject *target = stack.top(); - QmlParserStatus *status = - qobject_cast<QmlParserStatus *>(target); - - if (status) { - instr.type = QmlInstruction::BeginObject; - instr.begin.castValue = int(reinterpret_cast<char *>(status) - reinterpret_cast<char *>(target)); - --ii; - } else { - instr.type = QmlInstruction::NoOp; - } - } - break; - case QmlInstruction::BeginObject: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -436,25 +640,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::TryCompleteObject: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrTryCompleteObject> cc; -#endif - QObject *target = stack.top(); - QmlParserStatus *status = - qobject_cast<QmlParserStatus *>(target); - - if (status) { - instr.type = QmlInstruction::CompleteObject; - instr.complete.castValue = int(reinterpret_cast<char *>(status) - reinterpret_cast<char *>(target)); - --ii; - } else { - instr.type = QmlInstruction::NoOp; - } - } - break; - case QmlInstruction::CompleteObject: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -466,30 +651,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::AssignValueSource: - { - QObject *target = stack.at(stack.count() - 2); - int propIdx = instr.assignValueSource.property; - QByteArray pr; - if (propIdx == -1) { - pr = QmlMetaType::defaultProperty(target).name(); - if (pr.isEmpty()) - VME_EXCEPTION("Unable to resolve default property"); - } else { - pr = datas.at(propIdx); - } - - int coreIdx = qIndexOfProperty(target, pr); - if (coreIdx != -1) { - instr.type = QmlInstruction::StoreValueSource; - instr.assignValueSource.property = coreIdx; - ii--; - } else { - VME_EXCEPTION("Unknown property" << pr); - } - } - break; - case QmlInstruction::PushProperty: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -631,122 +792,46 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::AssignObject: + case QmlInstruction::StoreVariantObject: { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrAssignObject> cc; -#endif QObject *assign = stack.pop(); QObject *target = stack.top(); - QByteArray property; - if (instr.assignObject.property == -1) { - // XXX - optimize! - property = - QmlMetaType::defaultProperty(target).name(); - } else { - property = datas.at(instr.assignObject.property); - } - - int coreIdx = qIndexOfProperty(target, property); - - if (coreIdx != -1) { - QMetaProperty prop = - target->metaObject()->property(coreIdx); - int t = prop.userType(); - // XXX - optimize! - if (QmlMetaType::isList(t)) { - QVariant list = prop.read(target); - int listtype = QmlMetaType::listType(t); - QVariant v = QmlMetaType::fromObject(assign, listtype); - QmlMetaType::append(list, v); - } else if (QmlMetaType::isQmlList(t)) { - - // XXX - optimize! - QVariant list = prop.read(target); - QmlPrivate::ListInterface *li = - *(QmlPrivate::ListInterface **)list.constData(); - - int type = li->type(); - - const QMetaObject *mo = - QmlMetaType::rawMetaObjectForType(type); - - const QMetaObject *assignMo = assign->metaObject(); - bool found = false; - while(!found && assignMo) { - if (assignMo == mo) - found = true; - else - assignMo = assignMo->superClass(); - } - - if (!found) - VME_EXCEPTION("Cannot assign object to list"); - - // NOTE: This assumes a cast to QObject does not alter - // the object pointer - void *d = (void *)&assign; - li->append(d); - - } else if (QmlMetaType::isInterface(t)) { - const char *iid = QmlMetaType::interfaceIId(t); - bool ok = false; - if (iid) { - void *ptr = assign->qt_metacast(iid); - if (ptr) { - void *a[1]; - a[0] = &ptr; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - coreIdx, a); - ok = true; - } - } - - if (!ok) - VME_EXCEPTION("Cannot assign object to interface property" << property); - - } else if (prop.userType() == -1 /* means qvariant */) { - prop.write(target, qVariantFromValue(assign)); - } else { - const QMetaObject *propmo = - QmlMetaType::rawMetaObjectForType(t); - - bool isPropertyValue = false; - bool isAssignable = false; - const QMetaObject *c = assign->metaObject(); - while(c) { - isPropertyValue = isPropertyValue || (c == &QmlPropertyValueSource::staticMetaObject); - isAssignable = isAssignable || (c == propmo); - c = c->superClass(); - } - - if (isAssignable) { - // XXX - optimize! - QVariant v = QmlMetaType::fromObject(assign, t); - prop.write(target, v); - } else if (isPropertyValue) { - QmlPropertyValueSource *vs = - static_cast<QmlPropertyValueSource *>(assign); - vs->setParent(target); - vs->setTarget(QmlMetaProperty(target, coreIdx)); - } else { - VME_EXCEPTION("Cannot assign to" << property); - } - } + QVariant v = QVariant::fromValue(assign); + void *a[1]; + a[0] = (void *)&v; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeObject.propertyIndex, a); + } + break; + case QmlInstruction::StoreInterface: + { + QObject *assign = stack.pop(); + QObject *target = stack.top(); - } else { - if (instr.assignObject.property == -1) { - VME_EXCEPTION("Cannot assign to default property"); - } else { - VME_EXCEPTION("Cannot assign to non-existant property" << property); + int coreIdx = instr.storeObject.propertyIndex; + QMetaProperty prop = target->metaObject()->property(coreIdx); + int t = prop.userType(); + const char *iid = QmlMetaType::interfaceIId(t); + bool ok = false; + if (iid) { + void *ptr = assign->qt_metacast(iid); + if (ptr) { + void *a[1]; + a[0] = &ptr; + QMetaObject::metacall(target, + QMetaObject::WriteProperty, + coreIdx, a); + ok = true; } - } + } + if (!ok) + VME_EXCEPTION("Cannot assign object to interface property"); } break; - + case QmlInstruction::FetchAttached: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -797,30 +882,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::ResolveFetchObject: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrResolveFetchObject> cc; -#endif - QObject *target = stack.top(); - const QByteArray &pr = datas.at(instr.fetch.property); - int idx = qIndexOfProperty(target, pr); - if (idx == -1) - VME_EXCEPTION("Cannot resolve property" << pr); - QMetaProperty prop = target->metaObject()->property(idx); - instr.type = QmlInstruction::FetchObject; - instr.fetch.property = idx; - if (QmlMetaType::isObject(prop.userType())) { - instr.fetch.isObject = true; - } else if (prop.userType() == -1) { - instr.fetch.isObject = false; - } else { - VME_EXCEPTION("Cannot set properties on" << prop.name() << "as it is of unknown type"); - } - ii--; - } - break; - case QmlInstruction::FetchObject: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -871,37 +932,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::AssignStackObject: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrAssignStackObject> cc; -#endif - - QObject *obj = savedObjects[instr.assignStackObject.object]; - const QmlMetaProperty &prop = - pushedProperties.at(instr.assignStackObject.property); - - - const QMetaObject *mo = - QmlMetaType::rawMetaObjectForType(prop.propertyType()); - const QMetaObject *assignMo = obj->metaObject(); - - bool found = false; - while(!found && assignMo) { - if (assignMo == mo) - found = true; - else - assignMo = assignMo->superClass(); - } - - if (!found) - VME_EXCEPTION("Unable to assign object"); - - instr.type = QmlInstruction::StoreStackObject; - --ii; - } - break; - case QmlInstruction::StoreStackObject: { #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -921,19 +951,10 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in } break; - case QmlInstruction::NoOp: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrNoOp> cc; -#endif - } - break; - default: qFatal("QmlCompiledComponent: Internal error - unknown instruction %d", instr.type); break; } - } } if (isError()) { @@ -974,264 +995,4 @@ QList<QmlError> QmlVME::errors() const return vmeErrors; } -void QmlVME::runStoreInstruction(QStack<QObject *> &stack, - QmlInstruction &instr, - QmlCompiledData *comp) -{ - const QList<QString> &primitives = comp->primitives; - const QList<int> &intData = comp->intData; - const QList<float> &floatData = comp->floatData; - - switch(instr.type) { - case QmlInstruction::StoreVariant: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreVariant> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - // XXX - can be more efficient - QVariant v = QmlStringConverters::variantFromString(primitives.at(instr.storeString.value)); - a[0] = (void *)&v; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeString.propertyIndex, a); - } - break; - - case QmlInstruction::StoreString: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreString> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - a[0] = (void *)&primitives.at(instr.storeString.value); - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeString.propertyIndex, a); - } - break; - - case QmlInstruction::StoreReal: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreReal> cc; -#endif - QObject *target = stack.top(); - //### moc treats qreal properties as having type double - double r = static_cast<double>(instr.storeReal.value); - void *a[1]; - a[0] = &r; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeReal.propertyIndex, a); - } - break; - - case QmlInstruction::StoreBool: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreBool> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - a[0] = (void *)&instr.storeBool.value; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeBool.propertyIndex, a); - } - break; - - case QmlInstruction::StoreInteger: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreInteger> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - a[0] = (void *)&instr.storeInteger.value; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeReal.propertyIndex, a); - } - break; - - case QmlInstruction::StoreColor: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreColor> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QColor c = QColor::fromRgba(instr.storeColor.value); - a[0] = (void *)&c; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeColor.propertyIndex, a); - } - break; - - case QmlInstruction::StoreDate: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreDate> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QDate d = QDate::fromJulianDay(instr.storeDate.value); - a[0] = (void *)&d; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeDate.propertyIndex, a); - } - break; - - case QmlInstruction::StoreTime: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - //QFxCompilerTimer<QFxCompiler::InstrStoreTime> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QTime t; - t.setHMS(intData.at(instr.storeTime.valueIndex), - intData.at(instr.storeTime.valueIndex+1), - intData.at(instr.storeTime.valueIndex+2), - intData.at(instr.storeTime.valueIndex+3)); - a[0] = (void *)&t; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeTime.propertyIndex, a); - } - break; - - case QmlInstruction::StoreDateTime: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - //QFxCompilerTimer<QFxCompiler::InstrStoreDateTime> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QTime t; - t.setHMS(intData.at(instr.storeDateTime.valueIndex+1), - intData.at(instr.storeDateTime.valueIndex+2), - intData.at(instr.storeDateTime.valueIndex+3), - intData.at(instr.storeDateTime.valueIndex+4)); - QDateTime dt(QDate::fromJulianDay(intData.at(instr.storeDateTime.valueIndex)), t); - a[0] = (void *)&dt; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeDateTime.propertyIndex, a); - } - break; - - case QmlInstruction::StorePoint: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QPoint p = QPointF(floatData.at(instr.storeRealPair.valueIndex), - floatData.at(instr.storeRealPair.valueIndex+1)).toPoint(); - a[0] = (void *)&p; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRealPair.propertyIndex, a); - } - break; - - case QmlInstruction::StorePointF: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QPointF p(floatData.at(instr.storeRealPair.valueIndex), - floatData.at(instr.storeRealPair.valueIndex+1)); - a[0] = (void *)&p; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRealPair.propertyIndex, a); - } - break; - - case QmlInstruction::StoreSize: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QSize p = QSizeF(floatData.at(instr.storeRealPair.valueIndex), - floatData.at(instr.storeRealPair.valueIndex+1)).toSize(); - a[0] = (void *)&p; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRealPair.propertyIndex, a); - } - break; - - case QmlInstruction::StoreSizeF: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QSizeF s(floatData.at(instr.storeRealPair.valueIndex), - floatData.at(instr.storeRealPair.valueIndex+1)); - a[0] = (void *)&s; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRealPair.propertyIndex, a); - } - break; - - case QmlInstruction::StoreRect: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QRect r = QRectF(floatData.at(instr.storeRect.valueIndex), - floatData.at(instr.storeRect.valueIndex+1), - floatData.at(instr.storeRect.valueIndex+2), - floatData.at(instr.storeRect.valueIndex+3)).toRect(); - a[0] = (void *)&r; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRect.propertyIndex, a); - } - break; - - case QmlInstruction::StoreRectF: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; -#endif - QObject *target = stack.top(); - void *a[1]; - QRectF r(floatData.at(instr.storeRect.valueIndex), - floatData.at(instr.storeRect.valueIndex+1), - floatData.at(instr.storeRect.valueIndex+2), - floatData.at(instr.storeRect.valueIndex+3)); - a[0] = (void *)&r; - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeRect.propertyIndex, a); - } - break; - - case QmlInstruction::StoreObject: - { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxCompilerTimer<QFxCompiler::InstrStoreObject> cc; -#endif - QObject *assignObj = stack.pop(); - QObject *target = stack.top(); - - void *a[1]; - void *obj = (void *)(((char *)assignObj) + instr.storeObject.cast); - a[0] = (void *)&obj; - - QMetaObject::metacall(target, QMetaObject::WriteProperty, - instr.storeObject.propertyIndex, a); - } - break; - default: - qFatal("QmlCompiledComponent: Internal error - unknown instruction %d", instr.type); - break; - } - -} - QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvme_p.h b/src/declarative/qml/qmlvme_p.h index 4e5c6c1..f2ed576 100644 --- a/src/declarative/qml/qmlvme_p.h +++ b/src/declarative/qml/qmlvme_p.h @@ -64,9 +64,6 @@ public: QList<QmlError> errors() const; private: - void runStoreInstruction(QStack<QObject *> &stack, - QmlInstruction &, QmlCompiledData *); - QList<QmlError> vmeErrors; }; diff --git a/src/declarative/qml/rewriter/rewriter.cpp b/src/declarative/qml/rewriter/rewriter.cpp new file mode 100644 index 0000000..ec504fa --- /dev/null +++ b/src/declarative/qml/rewriter/rewriter.cpp @@ -0,0 +1,55 @@ +#include "rewriter_p.h" +#include "javascriptast_p.h" + +QT_BEGIN_NAMESPACE + +using namespace JavaScript; + +void Rewriter::replace(const AST::SourceLocation &loc, const QString &text) +{ replace(loc.offset, loc.length, text); } + +void Rewriter::remove(const AST::SourceLocation &loc) +{ return replace(loc.offset, loc.length, QString()); } + +void Rewriter::remove(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc) +{ return replace(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, QString()); } + +void Rewriter::insertTextBefore(const AST::SourceLocation &loc, const QString &text) +{ replace(loc.offset, 0, text); } + +void Rewriter::insertTextAfter(const AST::SourceLocation &loc, const QString &text) +{ replace(loc.offset + loc.length, 0, text); } + +void Rewriter::replace(int offset, int length, const QString &text) +{ textWriter.replace(offset, length, text); } + +void Rewriter::insertText(int offset, const QString &text) +{ replace(offset, 0, text); } + +void Rewriter::removeText(int offset, int length) +{ replace(offset, length, QString()); } + +QString Rewriter::textAt(const AST::SourceLocation &loc) const +{ return _code.mid(loc.offset, loc.length); } + +QString Rewriter::textAt(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc) const +{ return _code.mid(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset); } + +void Rewriter::accept(JavaScript::AST::Node *node) +{ JavaScript::AST::Node::acceptChild(node, this); } + +void Rewriter::moveTextBefore(const AST::SourceLocation &firstLoc, + const AST::SourceLocation &lastLoc, + const AST::SourceLocation &loc) +{ + textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset); +} + +void Rewriter::moveTextAfter(const AST::SourceLocation &firstLoc, + const AST::SourceLocation &lastLoc, + const AST::SourceLocation &loc) +{ + textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/rewriter.pri b/src/declarative/qml/rewriter/rewriter.pri new file mode 100644 index 0000000..987e26d --- /dev/null +++ b/src/declarative/qml/rewriter/rewriter.pri @@ -0,0 +1,4 @@ + +INCLUDEPATH += $$PWD +HEADERS += $$PWD/rewriter_p.h $$PWD/textwriter_p.h +SOURCES += $$PWD/rewriter.cpp $$PWD/textwriter.cpp diff --git a/src/declarative/qml/rewriter/rewriter_p.h b/src/declarative/qml/rewriter/rewriter_p.h new file mode 100644 index 0000000..892c006 --- /dev/null +++ b/src/declarative/qml/rewriter/rewriter_p.h @@ -0,0 +1,112 @@ +#ifndef REWRITER_H +#define REWRITER_H + +#include <QtCore/QList> +#include <QtCore/QString> + +#include "textwriter_p.h" +#include "javascriptastvisitor_p.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +namespace JavaScript { + +//////////////////////////////////////////////////////////////////////////////// +// Replacement +//////////////////////////////////////////////////////////////////////////////// +class Replacement +{ + int _offset; + int _length; + QString _text; + +public: + Replacement(int offset = 0, int length = 0, const QString &text = QString()) + : _offset(offset), _length(length), _text(text) + { } + + bool isNull() const { return _offset == _length; } + operator bool() const { return ! isNull(); } + + int offset() const { return _offset; } + int length() const { return _length; } + QString text() const { return _text; } +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// Rewriter +//////////////////////////////////////////////////////////////////////////////// +class Rewriter: public AST::Visitor +{ +protected: + TextWriter textWriter; +public: + // + // Token based API + // + + /// Returns the text of the token at the given \a location. + QString textAt(const AST::SourceLocation &location) const; + + QString textAt(const AST::SourceLocation &firstLoc, + const AST::SourceLocation &lastLoc) const; + + /// Replace the token at \a loc with the given \a text. + void replace(const AST::SourceLocation &loc, const QString &text); + + /// Remove the token at the given \a location. + void remove(const AST::SourceLocation &location); + + /// Remove all tokens in the range [\a firstLoc, \a lastLoc]. + void remove(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc); + + /// Insert \a text before the token at the given \a location. + void insertTextBefore(const AST::SourceLocation &location, const QString &text); + + /// Insert \a text after the token at the given \a location. + void insertTextAfter(const AST::SourceLocation &loc, const QString &text); + + void moveTextBefore(const AST::SourceLocation &firstLoc, + const AST::SourceLocation &lastLoc, + const AST::SourceLocation &loc); + + void moveTextAfter(const AST::SourceLocation &firstLoc, + const AST::SourceLocation &lastLoc, + const AST::SourceLocation &loc); + + // + // low-level offset based API + // + void replace(int offset, int length, const QString &text); + void insertText(int offset, const QString &text); + void removeText(int offset, int length); + + /// Visit the given \a node. + void accept(AST::Node *node); + + /// Returns the original unchanged source code. + QString code() const { return _code; } + + /// Returns the list of replacements. + QList<Replacement> replacementList() const { return _replacementList; } + +protected: + /// \internal + void setCode(const QString &code) { _code = code; } + +private: + QString _code; + QList<Replacement> _replacementList; +}; + +} // end of namespace JavaScript + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // REWRITER_H diff --git a/src/declarative/qml/rewriter/textwriter.cpp b/src/declarative/qml/rewriter/textwriter.cpp new file mode 100644 index 0000000..d56c9a1 --- /dev/null +++ b/src/declarative/qml/rewriter/textwriter.cpp @@ -0,0 +1,176 @@ +#include "textwriter_p.h" + +QT_BEGIN_NAMESPACE + +using namespace JavaScript; + +TextWriter::TextWriter() + :string(0), cursor(0) +{ +} + +static bool overlaps(int posA, int lengthA, int posB, int lengthB) { + return (posA < posB + lengthB && posA + lengthA > posB + lengthB) + || (posA < posB && posA + lengthA > posB); +} + +bool TextWriter::hasOverlap(int pos, int length) +{ + { + QListIterator<Replace> i(replaceList); + while (i.hasNext()) { + const Replace &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + } + { + QListIterator<Move> i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + return false; + } +} + +bool TextWriter::hasMoveInto(int pos, int length) +{ + QListIterator<Move> i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (cmd.to >= pos && cmd.to < pos + length) + return true; + } + return false; +} + +void TextWriter::replace(int pos, int length, const QString &replacement) +{ + Q_ASSERT(!hasOverlap(pos, length)); + Q_ASSERT(!hasMoveInto(pos, length)); + + Replace cmd; + cmd.pos = pos; + cmd.length = length; + cmd.replacement = replacement; + replaceList += cmd; +} + +void TextWriter::move(int pos, int length, int to) +{ + Q_ASSERT(!hasOverlap(pos, length)); + + Move cmd; + cmd.pos = pos; + cmd.length = length; + cmd.to = to; + moveList += cmd; +} + +void TextWriter::doReplace(const Replace &replace) +{ + int diff = replace.replacement.size() - replace.length; + { + QMutableListIterator<Replace> i(replaceList); + while (i.hasNext()) { + Replace &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + } + } + { + QMutableListIterator<Move> i(moveList); + while (i.hasNext()) { + Move &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + + if (replace.pos < c.to) + c.to += diff; + } + } + + if (string) { + string->replace(replace.pos, replace.length, replace.replacement); + } else if (cursor) { + cursor->setPosition(replace.pos); + cursor->setPosition(replace.pos + replace.length, QTextCursor::KeepAnchor); + cursor->insertText(replace.replacement); + } +} + +void TextWriter::doMove(const Move &move) +{ + QString text; + if (string) { + text = string->mid(move.pos, move.length); + } else if (cursor) { + cursor->setPosition(move.pos); + cursor->setPosition(move.pos + move.length, QTextCursor::KeepAnchor); + text = cursor->selectedText(); + } + + Replace cut; + cut.pos = move.pos; + cut.length = move.length; + Replace paste; + paste.pos = move.to; + paste.length = 0; + paste.replacement = text; + + replaceList.append(cut); + replaceList.append(paste); + + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } +} + +void TextWriter::write(QString *s) +{ + string = s; + write_helper(); + string = 0; +} + +void TextWriter::write(QTextCursor *textCursor) +{ + cursor = textCursor; + write_helper(); + cursor = 0; +} + +void TextWriter::write_helper() +{ + if (cursor) + cursor->beginEditBlock(); + { + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } + } + { + Move cmd; + while (!moveList.isEmpty()) { + cmd = moveList.first(); + moveList.removeFirst(); + doMove(cmd); + } + } + if (cursor) + cursor->endEditBlock(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/textwriter_p.h b/src/declarative/qml/rewriter/textwriter_p.h new file mode 100644 index 0000000..52d18d3 --- /dev/null +++ b/src/declarative/qml/rewriter/textwriter_p.h @@ -0,0 +1,60 @@ +#ifndef TEXTWRITER_H +#define TEXTWRITER_H + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtGui/QTextCursor> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +namespace JavaScript { + +class TextWriter +{ + QString *string; + QTextCursor *cursor; + + struct Replace { + int pos; + int length; + QString replacement; + }; + + QList<Replace> replaceList; + + struct Move { + int pos; + int length; + int to; + }; + + QList<Move> moveList; + + bool hasOverlap(int pos, int length); + bool hasMoveInto(int pos, int length); + + void doReplace(const Replace &replace); + void doMove(const Move &move); + + void write_helper(); + +public: + TextWriter(); + + void replace(int pos, int length, const QString &replacement); + void move(int pos, int length, int to); + + void write(QString *s); + void write(QTextCursor *textCursor); + +}; + +} // end of namespace JavaScript + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // TEXTWRITER_H |