From 7af1c2dc0e160652a9583cf1798720cd0f3d72f8 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 8 Oct 2015 10:06:06 +0200 Subject: Give SquareButton types a side property to mediate being square. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We shouldn't be setting width and height for each square; we should only need to set their side, which determines their width and height - so that they're actually square ! More pertinently, this could later serve as an illustration of how to use properties and bindings. Ideally, we'd do this in some way that actually ensures setting width or height changes side; but the simple approach will do for now. Change-Id: I0df89e11221dde5d1d2930c4a779b59cf0d46654 Reviewed-by: Topi Reiniö --- src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc | 8 +++++--- src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc | 3 ++- src/qml/doc/src/qmllanguageref/syntax/signals.qdoc | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc index 4b4b25796c..03a7ddfb19 100644 --- a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc @@ -43,7 +43,8 @@ For example, below is a document that declares a \l Rectangle with a child \l Mo import QtQuick 2.0 Rectangle { - width: 100; height: 100 + property int side: 100 + width: side; height: side color: "red" MouseArea { @@ -87,7 +88,7 @@ For example, the root object type in the \c SquareButton.qml file above is \l Re import QtQuick 2.0 Column { - SquareButton { width: 50; height: 50 } + SquareButton { side: 50 } SquareButton { x: 50; color: "blue" } SquareButton { radius: 10 } } @@ -112,7 +113,8 @@ Rectangle { root.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1) } - width: 100; height: 100 + property int side: 100 + width: side; height: side color: "red" MouseArea { diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index b0e262d2b9..b136a0e92f 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -724,7 +724,8 @@ Rectangle { signal activated(real xPosition, real yPosition) signal deactivated - width: 100; height: 100 + property int side: 100 + width: side; height: side MouseArea { anchors.fill: parent diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc index 602b202ed4..4a022f2b0b 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc @@ -187,7 +187,8 @@ Rectangle { signal activated(real xPosition, real yPosition) - width: 100; height: 100 + property int side: 100 + width: side; height: side MouseArea { anchors.fill: parent -- cgit v1.2.3 From e1a303dd01f2e7faa3ec54386ecbbfe113996790 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 20 Sep 2016 12:53:31 +0200 Subject: Enhance quickwidget example with all grabbing cases Add a demo of QQuickItem::grabToImage(), in addition to grabFramebuffer() and render(). This way all possible approaches are demonstrated and tested. Task-number: QTBUG-55879 Change-Id: I13c427730c416f0d87f83092627e2cb46aba2cc4 Reviewed-by: Shawn Rutledge Reviewed-by: Andy Nichols --- examples/quick/quickwidgets/quickwidget/main.cpp | 30 ++++++++++++++++------ .../quickwidgets/quickwidget/rotatingsquare.qml | 6 +++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp index 65258d958e..3f3638a43f 100644 --- a/examples/quick/quickwidgets/quickwidget/main.cpp +++ b/examples/quick/quickwidgets/quickwidget/main.cpp @@ -39,6 +39,7 @@ ****************************************************************************/ #include +#include #include #include @@ -50,8 +51,9 @@ public: private slots: void quickWidgetStatusChanged(QQuickWidget::Status); void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message); - void grabToFile(); - void renderToFile(); + void grabFramebuffer(); + void renderToPixmap(); + void grabToImage(); private: QQuickWidget *m_quickWidget; @@ -91,8 +93,9 @@ MainWindow::MainWindow() setCentralWidget(centralWidget); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); - fileMenu->addAction(tr("Grab to imFage"), this, &MainWindow::grabToFile); - fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToFile); + fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer); + fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap); + fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage); fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit); } @@ -113,8 +116,7 @@ void MainWindow::sceneGraphError(QQuickWindow::SceneGraphError, const QString &m template void saveToFile(QWidget *parent, T *saveable) { - QString t; - QFileDialog fd(parent, t, QString()); + QFileDialog fd(parent); fd.setAcceptMode(QFileDialog::AcceptSave); fd.setDefaultSuffix("png"); fd.selectFile("test.png"); @@ -122,19 +124,31 @@ template void saveToFile(QWidget *parent, T *saveable) saveable->save(fd.selectedFiles().first()); } -void MainWindow::grabToFile() +void MainWindow::grabFramebuffer() { QImage image = m_quickWidget->grabFramebuffer(); saveToFile(this, &image); } -void MainWindow::renderToFile() +void MainWindow::renderToPixmap() { QPixmap pixmap(m_quickWidget->size()); m_quickWidget->render(&pixmap); saveToFile(this, &pixmap); } +void MainWindow::grabToImage() +{ + QFileDialog fd(this); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setDefaultSuffix("png"); + fd.selectFile("test_grabToImage.png"); + if (fd.exec() == QDialog::Accepted) { + QMetaObject::invokeMethod(m_quickWidget->rootObject(), "performLayerBasedGrab", + Q_ARG(QVariant, fd.selectedFiles().first())); + } +} + int main(int argc, char **argv) { QApplication app(argc, argv); diff --git a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml index 0c25eddf88..03b1114d20 100644 --- a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml +++ b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml @@ -57,4 +57,10 @@ Rectangle { anchors.centerIn: parent text: "Qt Quick running in a widget" } + + function performLayerBasedGrab(fn) { + root.grabToImage(function(result) { + result.saveToFile(fn); + }); + } } -- cgit v1.2.3 From ad48b299b7fe0bbed2749adc66725e4f12661f79 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 16 Sep 2016 14:30:14 +0200 Subject: Add a facility to version type information for debugging This serves the same purpose as qtbase/corelib/global/qhooks.cpp, but is meant to be in sync with changes in Qt Declarative internals. Change-Id: I5a4a7d9ca5c340367581749e05d09380590c46fb Reviewed-by: Ulf Hermann --- src/qml/debugger/qqmldebug.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 35dc110e9a..ea98bb16fa 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -119,4 +119,22 @@ bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, return false; } +enum { HookCount = 3 }; + +// Only add to the end, and bump version if you do. +quintptr Q_QML_EXPORT qtDeclarativeHookData[] = { + // Version of this Array. Bump if you add to end. + 1, + + // Number of entries in this array. + HookCount, + + // TypeInformationVersion, an integral value, bumped whenever private + // object sizes or member offsets that are used in Qt Creator's + // data structure "pretty printing" change. + 1 +}; + +Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0])); + QT_END_NAMESPACE -- cgit v1.2.3 From e05fbf5c8bf636db5c500c3371d673de17de8c33 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Fri, 16 Sep 2016 17:32:58 +1000 Subject: Fix MouseArea sticky grab with drag.filterChildren enabled Task-number: QTBUG-56036 Change-Id: Iad776f42cc776e0d397173b3d2f3922eb7914392 Reviewed-by: Andrew den Exter --- src/quick/items/qquickmousearea.cpp | 2 + .../data/nestedFlickableStopAtBounds.qml | 44 ++++++++++ .../quick/qquickmousearea/tst_qquickmousearea.cpp | 93 ++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index d66e55aa12..a8a889d47b 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -666,6 +666,7 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event) Q_D(QQuickMouseArea); d->moved = false; d->stealMouse = d->preventStealing; + d->overThreshold = false; if (!d->enabled || !(event->button() & acceptedMouseButtons())) { QQuickItem::mousePressEvent(event); } else { @@ -944,6 +945,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) if (!d->pressed) { // no other buttons are pressed d->stealMouse = false; + d->overThreshold = false; if (c && c->mouseGrabberItem() == this) ungrabMouse(); emit canceled(); diff --git a/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml b/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml new file mode 100644 index 0000000000..0d5b496766 --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml @@ -0,0 +1,44 @@ +import QtQuick 2.5 + + +Rectangle { + width: 240 + height: 320 + + MouseArea { + objectName: "mouseArea" + anchors.fill: parent + + drag.target: moveable + drag.filterChildren: true + + Rectangle { + id: moveable + objectName: "moveable" + color: "red" + x: 50 + y: 80 + width: 200 + height: 200 + } + + Flickable { + objectName: "flickable" + anchors.fill: parent + contentHeight: 450 + + Rectangle { + x: 100 + y: 50 + width: 50 + height: 350 + color: "yellow" + } + + MouseArea { + width: 240 + height: 450 + } + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index d51c228b7c..38253f6ac8 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -117,6 +117,7 @@ private slots: void moveAndReleaseWithoutPress(); void nestedStopAtBounds(); void nestedStopAtBounds_data(); + void nestedFlickableStopAtBounds(); void containsPress_data(); void containsPress(); @@ -1736,6 +1737,98 @@ void tst_QQuickMouseArea::nestedStopAtBounds() QTest::mouseRelease(&view, Qt::LeftButton, 0, position); } +void tst_QQuickMouseArea::nestedFlickableStopAtBounds() +{ + QQuickView view; + QByteArray errorMessage; + QVERIFY2(initView(view, testFileUrl("nestedFlickableStopAtBounds.qml"), false, &errorMessage), errorMessage.constData()); + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + + QQuickMouseArea *mouseArea = view.rootObject()->findChild("mouseArea"); + QVERIFY(mouseArea); + + QQuickFlickable *flickable = mouseArea->findChild("flickable"); + QVERIFY(flickable); + + const int threshold = qApp->styleHints()->startDragDistance(); + + QPoint position(200, 280); + int &pos = position.ry(); + + // Drag up - should move the Flickable to end + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + pos -= threshold * 2; + QTest::mouseMove(&view, position); + pos -= threshold * 2; + QTest::mouseMove(&view, position); + QTest::qWait(10); + pos -= 150; + QTest::mouseMove(&view, position); + QVERIFY(flickable->isDragging()); + QVERIFY(!mouseArea->drag()->active()); + QCOMPARE(flickable->isAtYEnd(), true); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); + + QTRY_VERIFY(!flickable->isMoving()); + + pos = 280; + + // Drag up again - should activate MouseArea drag + QVERIFY(!mouseArea->drag()->active()); + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + pos -= threshold * 2; + QTest::mouseMove(&view, position); + pos -= threshold * 2; + QTest::mouseMove(&view, position); + QTest::qWait(10); + pos -= 20; + QTest::mouseMove(&view, position); + QVERIFY(mouseArea->drag()->active()); + QCOMPARE(flickable->isAtYEnd(), true); + QVERIFY(!flickable->isDragging()); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); + + // Drag to the top and verify that the MouseArea doesn't steal the grab when we drag back (QTBUG-56036) + pos = 50; + + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + pos += threshold; + QTest::mouseMove(&view, position); + pos += threshold; + QTest::mouseMove(&view, position); + QTest::qWait(10); + pos += 150; + QTest::mouseMove(&view, position); + QVERIFY(flickable->isDragging()); + QVERIFY(!mouseArea->drag()->active()); + QCOMPARE(flickable->isAtYBeginning(), true); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); + + QTRY_VERIFY(!flickable->isMoving()); + + pos = 280; + + // Drag up again - should not activate MouseArea drag + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + pos -= threshold; + QTest::mouseMove(&view, position); + pos -= threshold; + QTest::mouseMove(&view, position); + QTest::qWait(10); + pos -= 100; + QTest::mouseMove(&view, position); + QVERIFY(flickable->isDragging()); + QVERIFY(!mouseArea->drag()->active()); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); +} + void tst_QQuickMouseArea::containsPress_data() { QTest::addColumn("hoverEnabled"); -- cgit v1.2.3 From f5e2783acec1f46070330fc1ce90cb16901a5533 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 21 Sep 2016 12:29:50 +0200 Subject: Quick: Do not send SG updates when AnimatedSprite is not visible Task-number: QTBUG-55935 Change-Id: I475c1bb3e7aae9499b1b07a52f3c10f54c8b3481 Reviewed-by: Gunnar Sletta --- src/quick/items/qquickanimatedsprite.cpp | 32 ++++++++++++++++++++------------ src/quick/items/qquickanimatedsprite_p.h | 1 + 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 44b5ff1e45..7fd89e21a1 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -35,6 +35,7 @@ #include "qquicksprite_p.h" #include "qquickspriteengine_p.h" #include +#include #include #include #include @@ -362,7 +363,7 @@ void QQuickAnimatedSprite::start() } emit currentFrameChanged(0); emit runningChanged(true); - update(); + maybeUpdate(); } void QQuickAnimatedSprite::stop() @@ -372,7 +373,7 @@ void QQuickAnimatedSprite::stop() return; m_pauseOffset = 0; emit runningChanged(false); - update(); + maybeUpdate(); } /*! @@ -390,7 +391,15 @@ void QQuickAnimatedSprite::advance(int frames) m_curFrame += m_spriteEngine->maxFrames(); m_curFrame = m_curFrame % m_spriteEngine->maxFrames(); emit currentFrameChanged(m_curFrame); - update(); + maybeUpdate(); +} + +void QQuickAnimatedSprite::maybeUpdate() +{ + QQuickItemPrivate *priv = QQuickItemPrivate::get(this); + const QLazilyAllocated &extraData = priv->extra; + if ((extraData.isAllocated() && extraData->effectRefCount > 0) || priv->effectiveVisible) + update(); } /*! @@ -408,7 +417,7 @@ void QQuickAnimatedSprite::pause() m_pauseOffset = m_timestamp.elapsed(); m_paused = true; emit pausedChanged(true); - update(); + maybeUpdate(); } /*! @@ -426,7 +435,7 @@ void QQuickAnimatedSprite::resume() m_pauseOffset = m_pauseOffset - m_timestamp.elapsed(); m_paused = false; emit pausedChanged(false); - update(); + maybeUpdate(); } void QQuickAnimatedSprite::createEngine() @@ -438,7 +447,6 @@ void QQuickAnimatedSprite::createEngine() m_spriteEngine = new QQuickSpriteEngine(QList(spriteList), this); m_spriteEngine->startAssemblingImage(); reset(); - update(); } static QSGGeometry::Attribute AnimatedSprite_Attributes[] = { @@ -476,10 +484,10 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() return 0; } else if (m_spriteEngine->status() == QQuickPixmap::Null) { m_spriteEngine->startAssemblingImage(); - update();//Schedule another update, where we will check again + maybeUpdate();//Schedule another update, where we will check again return 0; } else if (m_spriteEngine->status() == QQuickPixmap::Loading) { - update();//Schedule another update, where we will check again + maybeUpdate();//Schedule another update, where we will check again return 0; } @@ -541,7 +549,7 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() void QQuickAnimatedSprite::reset() { m_pleaseReset = true; - update(); + maybeUpdate(); } QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) @@ -562,7 +570,7 @@ QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNode if (m_running) { if (!m_paused) - update(); + maybeUpdate(); if (node) { node->markDirty(QSGNode::DirtyMaterial); @@ -618,7 +626,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node) frameAt = 0; m_running = false; emit runningChanged(false); - update(); + maybeUpdate(); } } else { frameAt = m_curFrame; @@ -626,7 +634,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node) if (m_curFrame != lastFrame) { if (isCurrentFrameChangedConnected()) emit currentFrameChanged(m_curFrame); - update(); + maybeUpdate(); } qreal frameCount = m_spriteEngine->spriteFrames(); diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h index 5b181640f9..01c5c2d3bd 100644 --- a/src/quick/items/qquickanimatedsprite_p.h +++ b/src/quick/items/qquickanimatedsprite_p.h @@ -360,6 +360,7 @@ protected: void componentComplete() Q_DECL_OVERRIDE; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; private: + void maybeUpdate(); bool isCurrentFrameChangedConnected(); void prepareNextFrame(QSGGeometryNode *node); void reloadImage(); -- cgit v1.2.3 From 79121a0827df365468a02db20179d2c2d5ae720d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 25 Sep 2016 22:11:12 +0200 Subject: QSGRenderContext: Add null-checks for strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems, glGetString returns null for some reason, which causes a segfault here. Let's assume it's not one of the broken configurations and hope for the best instead. Task-number: QTCREATORBUG-15992 Task-number: QTBUG-56165 Change-Id: I83867e42f0fd8f576bf51ac0a2213e1348111ffd Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Michael Brüning Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/qsgcontext.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index be228e87c7..2580809b27 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -625,12 +625,12 @@ void QSGRenderContext::initialize(QOpenGLContext *context) #ifdef Q_OS_LINUX const char *vendor = (const char *) funcs->glGetString(GL_VENDOR); - if (strstr(vendor, "nouveau")) + if (vendor && strstr(vendor, "nouveau")) m_brokenIBOs = true; const char *renderer = (const char *) funcs->glGetString(GL_RENDERER); - if (strstr(renderer, "llvmpipe")) + if (renderer && strstr(renderer, "llvmpipe")) m_serializedRender = true; - if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16")) + if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16")) m_brokenIBOs = true; #endif -- cgit v1.2.3 From 2afb54fb51091765f79548b0b057795bc3c6eb38 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 23 Sep 2016 11:34:12 +0200 Subject: V4: Free up 2 address bits in 64bit mode This allows for the OS to use 49 address bits. It also maps JS Undefined to the C++ nullptr on 64bit. Task-number: QTBUG-54822 Change-Id: I7cc90620f499be1506a61aac77d72d067308838c Reviewed-by: Lars Knoll --- src/qml/debugger/qqmldebug.cpp | 2 +- src/qml/jit/qv4assembler.cpp | 38 +++++- src/qml/jit/qv4assembler_p.h | 5 + src/qml/jit/qv4isel_masm.cpp | 98 ++++++++++---- src/qml/jit/qv4isel_masm_p.h | 4 +- src/qml/jsruntime/qv4scopedvalue_p.h | 2 +- src/qml/jsruntime/qv4value_p.h | 246 ++++++++++++++++++++--------------- src/qml/jsruntime/qv4vme_moth.cpp | 26 ++-- 8 files changed, 269 insertions(+), 152 deletions(-) diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index ea98bb16fa..e864469da6 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -132,7 +132,7 @@ quintptr Q_QML_EXPORT qtDeclarativeHookData[] = { // TypeInformationVersion, an integral value, bumped whenever private // object sizes or member offsets that are used in Qt Creator's // data structure "pretty printing" change. - 1 + 2 }; Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0])); diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 929726f4b7..b7dbc81f90 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -133,8 +133,30 @@ void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBl generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); } -void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left,TrustedImm32 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, +#ifdef QV4_USE_64_BIT_VALUE_ENCODING +void Assembler::generateCJumpOnCompare(RelationalCondition cond, + RegisterID left, + TrustedImm64 right, + IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) +{ + if (trueBlock == _nextBlock) { + Jump target = branch64(invert(cond), left, right); + addPatch(falseBlock, target); + } else { + Jump target = branch64(cond, left, right); + addPatch(trueBlock, target); + jumpToBlock(currentBlock, falseBlock); + } +} +#endif + +void Assembler::generateCJumpOnCompare(RelationalCondition cond, + RegisterID left, + TrustedImm32 right, + IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { if (trueBlock == _nextBlock) { @@ -147,8 +169,11 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left } } -void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, +void Assembler::generateCJumpOnCompare(RelationalCondition cond, + RegisterID left, + RegisterID right, + IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { if (trueBlock == _nextBlock) { @@ -334,9 +359,8 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe // not an int, check if it's a double: isNoInt.link(this); #ifdef QV4_USE_64_BIT_VALUE_ENCODING - and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, - Assembler::TrustedImm32(0)); + rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); + Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0)); #else and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 532a3114f2..ba9ca66935 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -374,6 +374,11 @@ public: void addPatch(DataLabelPtr patch, IR::BasicBlock *target); void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right, + IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock); +#endif void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index b6df5fb08c..1913b398eb 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -703,7 +703,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target) #else _as->store32(Assembler::ReturnValueRegister, destAddr); destAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type), destAddr); + _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr); #endif } @@ -1103,7 +1103,7 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe // not an int, check if it's NOT a double: isNoInt.link(_as); #ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister); + _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister); Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); #else @@ -1194,10 +1194,15 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe _as->load64(addr, Assembler::ScratchRegister); _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - // check if it's a number - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ScratchRegister); - Assembler::Jump isInt = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(1)); - Assembler::Jump fallback = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); + // check if it's integer convertible + _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister); + Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3)); + + // nope, not integer convertible, so check for a double: + _as->urshift64(Assembler::TrustedImm32( + QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), + Assembler::ScratchRegister); + Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); // it's a double _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); @@ -1212,7 +1217,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, _as->loadAddress(Assembler::ScratchRegister, source)); - isInt.link(_as); + isIntConvertible.link(_as); success.link(_as); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { @@ -1784,9 +1789,9 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr { Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual); - if (visitCJumpStrictNullUndefined(IR::NullType, binop, trueBlock, falseBlock)) + if (visitCJumpStrictNull(binop, trueBlock, falseBlock)) return; - if (visitCJumpStrictNullUndefined(IR::UndefinedType, binop, trueBlock, falseBlock)) + if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock)) return; if (visitCJumpStrictBool(binop, trueBlock, falseBlock)) return; @@ -1802,16 +1807,14 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr } // Only load the non-null temp. -bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) +bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, + IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) { - Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType); - IR::Expr *varSrc = 0; - if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef) + if (binop->left->type == IR::VarType && binop->right->type == IR::NullType) varSrc = binop->left; - else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType) + else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType) varSrc = binop->right; if (!varSrc) return false; @@ -1822,7 +1825,7 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I } if (IR::Const *c = varSrc->asConst()) { - if (c->type == nullOrUndef) + if (c->type == IR::NullType) _as->jumpToBlock(_block, trueBlock); else _as->jumpToBlock(_block, falseBlock); @@ -1835,9 +1838,54 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I _as->load32(tagAddr, tagReg); Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::TrustedImm32 tag(nullOrUndef == IR::NullType ? int(QV4::Value::Null_Type_Internal) - : int(QV4::Value::Undefined_Type)); + : Assembler::NotEqual; + const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal); + _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); + return true; +} + +bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, + IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) +{ + IR::Expr *varSrc = 0; + if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType) + varSrc = binop->left; + else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType) + varSrc = binop->right; + if (!varSrc) + return false; + + if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) { + _as->jumpToBlock(_block, falseBlock); + return true; + } + + if (IR::Const *c = varSrc->asConst()) { + if (c->type == IR::UndefinedType) + _as->jumpToBlock(_block, trueBlock); + else + _as->jumpToBlock(_block, falseBlock); + return true; + } + + Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal + : Assembler::NotEqual; + const Assembler::RegisterID tagReg = Assembler::ScratchRegister; +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + _as->load64(addr, tagReg); + const Assembler::TrustedImm64 tag(0); +#else // !QV4_USE_64_BIT_VALUE_ENCODING + Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + _as->load32(tagAddr, tagReg); + Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0)); + _as->addPatch(falseBlock, j); + + tagAddr.offset += 4; + _as->load32(tagAddr, tagReg); + const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal); +#endif _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } @@ -1928,10 +1976,14 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal))); - Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type))); + Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal))); + tagAddr.offset -= 4; + _as->load32(tagAddr, tagReg); + Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0)); _as->addPatch(trueBlock, isNull); - _as->addPatch(trueBlock, isUndefined); - _as->jumpToBlock(_block, falseBlock); + _as->addPatch(falseBlock, isNotUndefinedTag); + _as->addPatch(falseBlock, isNotUndefinedValue); + _as->jumpToBlock(_block, trueBlock); return true; } diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 6e9b02b034..f6d9364066 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -166,8 +166,8 @@ protected: bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - bool visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, - IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); + bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); + bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 0b063ee4b8..ca23f83cfd 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -309,7 +309,7 @@ struct ScopedCallData { { int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); ptr = reinterpret_cast(scope.alloc(size)); - ptr->tag = QV4::Value::Integer_Type; + ptr->tag = QV4::Value::Integer_Type_Internal; ptr->argc = argc; } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 5abf5ad9e8..02425c52a5 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -70,23 +70,85 @@ private: /* We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. - In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN) - is indicated by a number that has the top 13 bits set. The other values are usually set to 0 by the - processor, and are thus free for us to store other data. We keep pointers in there for managed objects, - and encode the other types using the free space given to use by the unused bits for NaN values. This also - works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory. - - On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that - will make the number a NaN. The Masks below are used for encoding the other types. - - On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded - with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These - can be used, as the highest valid pointer on a 64 bit system is 2^48-1. - - If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer. - This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set). - - Bit 15-17 is then used to encode other immediates. + In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double + NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a + signalling NaN it is the top 14 bits. The other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for + managed objects, and encode the other types using the free space given to use by the unused + bits for NaN values. This also works for pointers on 64 bit systems, as they all currently + only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for + pointers.) + + On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value + that will make the number a NaN. The Masks below are used for encoding the other types. + + On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between + managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave + the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is + set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are + used to encode Null/Int/Bool. + + On both 32bit and 64bit, Undefined is encoded as a managed pointer with value 0. This is + the same as a nullptr. + + Specific bit-sequences: + 0 = always 0 + 1 = always 1 + x = stored value + a,b,c,d = specific bit values, see notes + + 64bit: + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 00000000 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - a: xor-ed signbit, always 1 for NaN + - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value + - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 + - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ + and JS + - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 + - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, + so: (val >> (64-15)) == 1 + - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where + any non double results in a NaN + + 32bit: + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool + 01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - the upper 32 bits are the tag, the lower 32 bits the value + - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value + - a: sign bit, always 0 for NaN + - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value + - d: stored double value, as long as not *all* of them are 1, because that's a boxed value + (see above) + - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined + and managed), and bit 49 set to 1 (where undefined and managed have it set to 0) + - Null, Bool, and Int have bit 48 set, indicating integer-convertible */ quint64 _val; @@ -137,7 +199,7 @@ public: { quint32 v; memcpy(&v, &b, 4); - setTagValue(Managed_Type, v); + setTagValue(Managed_Type_Internal, v); } #endif @@ -153,12 +215,32 @@ public: Q_ALWAYS_INLINE void setEmpty() { - setTagValue(Empty_Type, value()); + setTagValue(Empty_Type_Internal, value()); } Q_ALWAYS_INLINE void setEmpty(int i) { - setTagValue(Empty_Type, quint32(i)); + setTagValue(Empty_Type_Internal, quint32(i)); + } + + enum Type { + Undefined_Type, + Managed_Type, + Empty_Type, + Integer_Type, + Boolean_Type, + Null_Type, + Double_Type + }; + + inline Type type() const { + if (isUndefined()) return Undefined_Type; + if (isManaged()) return Managed_Type; + if (isEmpty()) return Empty_Type; + if (isInteger()) return Integer_Type; + if (isBoolean()) return Boolean_Type; + if (isNull()) return Null_Type; + Q_ASSERT(isDouble()); return Double_Type; } #ifndef QV4_USE_64_BIT_VALUE_ENCODING @@ -166,101 +248,64 @@ public: SilentNaNBit = 0x00040000, NaN_Mask = 0x7ff80000, NotDouble_Mask = 0x7ffa0000, - Type_Mask = 0xffffc000, - Immediate_Mask = NotDouble_Mask | 0x00004000 | SilentNaNBit, - IsNullOrUndefined_Mask = Immediate_Mask | 0x08000, + Immediate_Mask = NotDouble_Mask | 0x00020000u | SilentNaNBit, Tag_Shift = 32 }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x08000, - Integer_Type = Immediate_Mask | 0x18000, - Managed_Type = NotDouble_Mask | 0x00000 | SilentNaNBit, - Empty_Type = NotDouble_Mask | 0x18000 | SilentNaNBit - }; - - enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | 0x1 - }; - - enum ValueTypeInternal { - Null_Type_Internal = Null_Type | ConvertibleToInt, - Boolean_Type_Internal = Boolean_Type | ConvertibleToInt, - Integer_Type_Internal = Integer_Type | ConvertibleToInt, + enum { + Managed_Type_Internal = NotDouble_Mask }; #else - static const quint64 NaNEncodeMask = 0xffff800000000000ll; - static const quint64 IsInt32Mask = 0x0002000000000000ll; - static const quint64 IsDoubleMask = 0xfffc000000000000ll; - static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask; - static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll; - static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll; - static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask; + static const quint64 NaNEncodeMask = 0xfffc000000000000ll; + static const quint64 Immediate_Mask = 0x00020000u; // bit 49 enum Masks { NaN_Mask = 0x7ff80000, - Type_Mask = 0xffff8000, - IsDouble_Mask = 0xfffc0000, - Immediate_Mask = 0x00018000, - IsNullOrUndefined_Mask = 0x00008000, - IsNullOrBoolean_Mask = 0x00010000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = IsNullOrUndefined_Mask, - Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask, - Boolean_Type = IsNullOrBoolean_Mask, - Integer_Type = 0x20000|IsNullOrBoolean_Mask, - Managed_Type = 0, - Empty_Type = Undefined_Type | 0x4000 }; enum { IsDouble_Shift = 64-14, - IsNumber_Shift = 64-15, - IsConvertibleToInt_Shift = 64-16, - IsManaged_Shift = 64-17 + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-16, + Tag_Shift = 32, + IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, + Managed_Type_Internal = 0 }; - - +#endif enum ValueTypeInternal { - Null_Type_Internal = Null_Type, - Boolean_Type_Internal = Boolean_Type, - Integer_Type_Internal = Integer_Type + Empty_Type_Internal = Immediate_Mask | 0, + ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 + Null_Type_Internal = ConvertibleToInt | 0x08000u, + Boolean_Type_Internal = ConvertibleToInt | 0x04000u, + Integer_Type_Internal = ConvertibleToInt | 0x02000u }; -#endif - - inline unsigned type() const { - return tag() & Type_Mask; - } // used internally in property - inline bool isEmpty() const { return tag() == Empty_Type; } - - inline bool isUndefined() const { return tag() == Undefined_Type; } + inline bool isEmpty() const { return tag() == Empty_Type_Internal; } inline bool isNull() const { return tag() == Null_Type_Internal; } - inline bool isBoolean() const { return tag ()== Boolean_Type_Internal; } + inline bool isBoolean() const { return tag() == Boolean_Type_Internal; } + inline bool isInteger() const { return tag() == Integer_Type_Internal; } + inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } + inline bool isNumber() const { return isDouble() || isInteger(); } + #ifdef QV4_USE_64_BIT_VALUE_ENCODING - inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; } + inline bool isUndefined() const { return _val == 0; } inline bool isDouble() const { return (_val >> IsDouble_Shift); } - inline bool isNumber() const { return (_val >> IsNumber_Shift); } - inline bool isManaged() const { return !(_val >> IsManaged_Shift); } - inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; } - inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; } + inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); } + + inline bool integerCompatible() const { + return (_val >> IsIntegerConvertible_Shift) == 3; + } static inline bool integerCompatible(Value a, Value b) { return a.integerCompatible() && b.integerCompatible(); } static inline bool bothDouble(Value a, Value b) { return a.isDouble() && b.isDouble(); } - inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; } + inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } #else - inline bool isInteger() const { return tag() == Integer_Type_Internal; } + inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; } inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag() == Integer_Type_Internal || (tag() & NotDouble_Mask) != NotDouble_Mask; } - inline bool isManaged() const { return tag() == Managed_Type; } - inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; } + inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); } inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; } static inline bool integerCompatible(Value a, Value b) { return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt; @@ -500,14 +545,14 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value inline Primitive Primitive::undefinedValue() { Primitive v; - v.setTagValue(Undefined_Type, 0); + v.setM(Q_NULLPTR); return v; } inline Primitive Primitive::emptyValue() { Primitive v; - v.setTagValue(Value::Empty_Type, 0); + v.setEmpty(0); return v; } @@ -535,7 +580,6 @@ inline Primitive Primitive::fromDouble(double d) inline Primitive Primitive::fromInt32(int i) { Primitive v; - v.setTagValue(Integer_Type_Internal, 0); v.setInt_32(i); return v; } @@ -553,31 +597,23 @@ inline Primitive Primitive::fromUInt32(uint i) struct Encode { static ReturnedValue undefined() { - return quint64(Value::Undefined_Type) << Value::Tag_Shift; + return Primitive::undefinedValue().rawValue(); } static ReturnedValue null() { - return quint64(Value::Null_Type_Internal) << Value::Tag_Shift; + return Primitive::nullValue().rawValue(); } Encode(bool b) { - val = (quint64(Value::Boolean_Type_Internal) << Value::Tag_Shift) | (uint)b; + val = Primitive::fromBoolean(b).rawValue(); } Encode(double d) { - Value v; - v.setDouble(d); - val = v.rawValue(); + val = Primitive::fromDouble(d).rawValue(); } Encode(int i) { - val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | (uint)i; + val = Primitive::fromInt32(i).rawValue(); } Encode(uint i) { - if (i <= INT_MAX) { - val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | i; - } else { - Value v; - v.setDouble(i); - val = v.rawValue(); - } + val = Primitive::fromUInt32(i).rawValue(); } Encode(ReturnedValue v) { val = v; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 024a72bde2..4688908286 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -563,7 +563,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code #endif // DO_TRACE_INSTR Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::callValue(engine, VALUE(instr.dest), callData)); @@ -573,7 +573,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::callProperty(engine, instr.name, callData)); @@ -582,7 +582,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallPropertyLookup) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData)); @@ -592,7 +592,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData)); @@ -602,7 +602,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData)); @@ -611,7 +611,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallElement) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::callElement(engine, VALUE(instr.index), callData)); @@ -620,7 +620,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallActivationProperty) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::callActivationProperty(engine, instr.name, callData)); @@ -629,7 +629,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallGlobalLookup) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::callGlobalLookup(engine, instr.index, callData)); @@ -735,7 +735,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateValue) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::constructValue(engine, VALUE(instr.func), callData)); @@ -744,7 +744,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateProperty) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::constructProperty(engine, instr.name, callData)); @@ -753,7 +753,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(ConstructPropertyLookup) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::constructPropertyLookup(engine, instr.index, callData)); @@ -762,7 +762,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateActivationProperty) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::constructActivationProperty(engine, instr.name, callData)); @@ -771,7 +771,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(ConstructGlobalLookup) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type; + callData->tag = QV4::Value::Integer_Type_Internal; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::constructGlobalLookup(engine, instr.index, callData)); -- cgit v1.2.3 From ef8c6f6a0bf5e4c9ee41306f2df59048ab96038f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Sep 2016 16:49:10 +0200 Subject: Flickable: don't activate velocityTimeline if scroll phase available The velocity timeline does not need to drive the movement if we can be sure that there are enough wheel events coming from the OS to move the flickable smoothly. And when the velocityTimeline is not active, the movementEndingTimer will emit the movementEnded signal, as it should. Task-number: QTBUG-55871 Change-Id: I5569be3aa6335d43ba162967ee03d08de3ba8096 Reviewed-by: J-P Nurmi --- src/quick/items/qquickflickable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 760eeed452..7c45d1c5ad 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1616,7 +1616,7 @@ void QQuickFlickable::viewportMoved(Qt::Orientations orient) void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback) { - if (pressed || calcVelocity) { + if (!scrollingPhase && (pressed || calcVelocity)) { int elapsed = data.velocityTime.restart(); if (elapsed > 0) { qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed; -- cgit v1.2.3 From 3a45458b96bdcbccc189aabf668e998ea03be46f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Wed, 28 Sep 2016 00:09:05 +0200 Subject: Fix crash on Array.prototype.join.call(0) We (incorrectly) didn't check the return value to make sure we had a valid self. At the same time, rename the self variable to match up with other methods. Task-number: QTBUG-53672 Change-Id: Ia0ae5a553e49c4c3b2834c7fdf649fe6373951a2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arrayobject.cpp | 13 ++++++++----- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 9 +++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 25d3d9329b..324f2c7bf2 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -178,6 +178,10 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) { Scope scope(ctx); ScopedValue arg(scope, ctx->argument(0)); + ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine)); + + if (!instance) + return ctx->d()->engine->newString()->asReturnedValue(); QString r4; if (arg->isUndefined()) @@ -185,8 +189,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) else r4 = arg->toQString(); - ScopedObject self(scope, ctx->thisObject()); - ScopedValue length(scope, self->get(ctx->d()->engine->id_length())); + ScopedValue length(scope, instance->get(ctx->d()->engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); if (!r2) @@ -195,7 +198,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) QString R; // ### FIXME - if (ArrayObject *a = self->as()) { + if (ArrayObject *a = instance->as()) { ScopedValue e(scope); for (uint i = 0; i < a->getLength(); ++i) { if (i) @@ -212,7 +215,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) // crazy! // ScopedString name(scope, ctx->d()->engine->newString(QStringLiteral("0"))); - ScopedValue r6(scope, self->get(name)); + ScopedValue r6(scope, instance->get(name)); if (!r6->isNullOrUndefined()) R = r6->toQString(); @@ -221,7 +224,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) R += r4; name = Primitive::fromDouble(k).toString(scope.engine); - r12 = self->get(name); + r12 = instance->get(name); if (scope.hasException()) return Encode::undefined(); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 8594aec8cd..acaa6604f9 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -143,6 +143,7 @@ private slots: void arrayPop_QTBUG_35979(); void array_unshift_QTBUG_52065(); + void array_join_QTBUG_53672(); void regexpLastMatch(); void indexedAccesses(); @@ -3016,6 +3017,14 @@ void tst_QJSEngine::array_unshift_QTBUG_52065() QCOMPARE(result.property(i).toInt(), i); } +void tst_QJSEngine::array_join_QTBUG_53672() +{ + QJSEngine eng; + QJSValue result = eng.evaluate("Array.prototype.join.call(0)"); + QVERIFY(result.isString()); + QCOMPARE(result.toString(), QString("")); +} + void tst_QJSEngine::regexpLastMatch() { QJSEngine eng; -- cgit v1.2.3 From 7b84962c47c9618af49526e3a2ef8c1c969d5aaa Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Sep 2016 17:14:33 +0200 Subject: Flickable: do not emit movementEnded until it really does This was occurring when using a physical mouse wheel: movementEnded was emitted, then contentYChanged would still be emitted a few more times. Task-number: QTBUG-55886 Change-Id: Ib5e833d5d84633bb07b8c240ea3ccc9977e443f8 Reviewed-by: J-P Nurmi --- src/quick/items/qquickflickable.cpp | 2 +- tests/auto/quick/qquickflickable/data/wheel.qml | 5 +++++ .../quick/qquickflickable/tst_qquickflickable.cpp | 20 +++++++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 7c45d1c5ad..a5b4bb0309 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1563,7 +1563,7 @@ void QQuickFlickable::timerEvent(QTimerEvent *event) d->movementEndingTimer.stop(); d->pressed = false; d->stealMouse = false; - if (!d->velocityTimeline.isActive()) + if (!d->velocityTimeline.isActive() && !d->timeline.isActive()) movementEnding(true, true); } } diff --git a/tests/auto/quick/qquickflickable/data/wheel.qml b/tests/auto/quick/qquickflickable/data/wheel.qml index 2928bbcd72..2be543cdde 100644 --- a/tests/auto/quick/qquickflickable/data/wheel.qml +++ b/tests/auto/quick/qquickflickable/data/wheel.qml @@ -8,9 +8,14 @@ Rectangle { Flickable { id: flick objectName: "flick" + property bool ended: false + property int movementsAfterEnd: 0 anchors.fill: parent contentWidth: 800 contentHeight: 800 + onContentXChanged: if (ended) ++movementsAfterEnd + onContentYChanged: if (ended) ++movementsAfterEnd + onMovementEnded: ended = true Rectangle { width: flick.contentWidth diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 294f7069c0..7de0a0fa0e 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -724,7 +724,10 @@ void tst_qquickflickable::wheel() QQuickFlickable *flick = window->rootObject()->findChild("flick"); QVERIFY(flick != 0); + QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flick); + QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded())); + // test a vertical flick { QPoint pos(200, 200); QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); @@ -735,9 +738,19 @@ void tst_qquickflickable::wheel() QTRY_VERIFY(flick->contentY() > 0); QCOMPARE(flick->contentX(), qreal(0)); - flick->setContentY(0); + QTRY_COMPARE(moveEndSpy.count(), 1); + QCOMPARE(fp->velocityTimeline.isActive(), false); + QCOMPARE(fp->timeline.isActive(), false); + QTest::qWait(50); // make sure that onContentYChanged won't sneak in again + QCOMPARE(flick->property("movementsAfterEnd").value(), 0); // QTBUG-55886 + + // get ready to test horizontal flick + flick->setContentY(0); // which triggers movementEnded again + flick->setProperty("movementsAfterEnd", 0); + flick->setProperty("ended", false); QCOMPARE(flick->contentY(), qreal(0)); + // test a horizontal flick { QPoint pos(200, 200); QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier); @@ -748,6 +761,11 @@ void tst_qquickflickable::wheel() QTRY_VERIFY(flick->contentX() > 0); QCOMPARE(flick->contentY(), qreal(0)); + QTRY_COMPARE(moveEndSpy.count(), 2); + QCOMPARE(fp->velocityTimeline.isActive(), false); + QCOMPARE(fp->timeline.isActive(), false); + QTest::qWait(50); // make sure that onContentXChanged won't sneak in again + QCOMPARE(flick->property("movementsAfterEnd").value(), 0); // QTBUG-55886 } void tst_qquickflickable::movingAndFlicking_data() -- cgit v1.2.3 From 84d3a064d7ab331c42ec1ced6e658a8bcc32f0f7 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 29 Sep 2016 13:00:33 +0200 Subject: QQuickText: fix paddings when wrapping or eliding is used Change-Id: I8ec8c8eff41e77225ef42f7bd9e52f4558d00130 Task-number: QTBUG-55779 Reviewed-by: Liang Qi --- src/quick/items/qquicktext.cpp | 2 +- tests/auto/quick/qquicktext/tst_qquicktext.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index ea2d2f3133..044e6ce8c1 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -921,7 +921,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) bool wasInLayout = internalWidthUpdate; internalWidthUpdate = true; - q->setImplicitHeight(naturalHeight); + q->setImplicitHeight(naturalHeight + q->topPadding() + q->bottomPadding()); internalWidthUpdate = wasInLayout; multilineElide = elideMode == QQuickText::ElideRight diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 50c75046a6..7db9078a7b 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -4122,6 +4122,18 @@ void tst_qquicktext::padding() QCOMPARE(obj->bottomPadding(), 1.11); QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding()); + obj->setWidth(cw / 2); + obj->setElideMode(QQuickText::ElideRight); + QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding()); + QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding()); + obj->setElideMode(QQuickText::ElideNone); + obj->resetWidth(); + + obj->setWrapMode(QQuickText::WordWrap); + QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding()); + QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding()); + obj->setWrapMode(QQuickText::NoWrap); + obj->setText("Qt"); QVERIFY(obj->contentWidth() < cw); QCOMPARE(obj->contentHeight(), ch); -- cgit v1.2.3 From 79cfc8788d6267eeb263983466ba21758f618dcd Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 12 May 2016 14:56:20 +0200 Subject: Fix incorrectly aligned text whose size depends on its implicit size The if statement in QQuickTextPrivate::setupTextLayout() was missing an OR clause for the case where the line width is neither greater nor less than the old width/natural width, but has actually changed. If that sounds confusing, it's because it is. Basically, the outer layouting loop in that function needs to run twice for this scenario, so that's what this patch makes it do. Change-Id: I13777667eb13506d50f05e9766785a1c2c46125c Task-number: QTBUG-50738 Task-number: QTBUG-50740 Reviewed-by: Liang Qi --- src/quick/items/qquicktext.cpp | 4 +- .../data/hAlignWidthDependsOnImplicitWidth.qml | 25 +++++++++++ tests/auto/quick/qquicktext/tst_qquicktext.cpp | 48 ++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 044e6ce8c1..ee5965676c 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -882,11 +882,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) // If the width of the item has changed and it's possible the result of wrapping, // eliding, scaling has changed, or the text is not left aligned do another layout. - if ((lineWidth < qMin(oldWidth, naturalWidth) || (widthExceeded && lineWidth > oldWidth)) + if ((!qFuzzyCompare(lineWidth, oldWidth) || (widthExceeded && lineWidth > oldWidth)) && (singlelineElide || multilineElide || canWrap || horizontalFit || q->effectiveHAlign() != QQuickText::AlignLeft)) { widthChanged = true; - widthExceeded = false; + widthExceeded = lineWidth >= qMin(oldWidth, naturalWidth); heightExceeded = false; continue; } diff --git a/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml b/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml new file mode 100644 index 0000000000..4358a58f57 --- /dev/null +++ b/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml @@ -0,0 +1,25 @@ +import QtQuick 2.6 + +Item { + width: 200 + height: 200 + + property alias text: label.text + property alias horizontalAlignment: label.horizontalAlignment + property alias elide: label.elide + property int extraWidth: 0 + + Rectangle { + border.color: "red" + x: 100 + width: label.implicitWidth + extraWidth + height: label.implicitHeight + + Text { + id: label + anchors.fill: parent + text: 'press me' + horizontalAlignment: Text.AlignHCenter + } + } +} diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 7db9078a7b..bdff985027 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -151,6 +151,9 @@ private slots: void padding(); + void hAlignWidthDependsOnImplicitWidth_data(); + void hAlignWidthDependsOnImplicitWidth(); + private: QStringList standard; QStringList richText; @@ -4170,6 +4173,51 @@ void tst_qquicktext::padding() delete root; } +void tst_qquicktext::hAlignWidthDependsOnImplicitWidth_data() +{ + QTest::addColumn("horizontalAlignment"); + QTest::addColumn("elide"); + QTest::addColumn("extraWidth"); + + QTest::newRow("AlignHCenter, ElideNone, 0 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideNone << 0; + QTest::newRow("AlignRight, ElideNone, 0 extraWidth") << QQuickText::AlignRight << QQuickText::ElideNone << 0; + QTest::newRow("AlignHCenter, ElideRight, 0 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideRight << 0; + QTest::newRow("AlignRight, ElideRight, 0 extraWidth") << QQuickText::AlignRight << QQuickText::ElideRight << 0; + QTest::newRow("AlignHCenter, ElideNone, 20 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideNone << 20; + QTest::newRow("AlignRight, ElideNone, 20 extraWidth") << QQuickText::AlignRight << QQuickText::ElideNone << 20; + QTest::newRow("AlignHCenter, ElideRight, 20 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideRight << 20; + QTest::newRow("AlignRight, ElideRight, 20 extraWidth") << QQuickText::AlignRight << QQuickText::ElideRight << 20; +} + +void tst_qquicktext::hAlignWidthDependsOnImplicitWidth() +{ + QFETCH(QQuickText::HAlignment, horizontalAlignment); + QFETCH(QQuickText::TextElideMode, elide); + QFETCH(int, extraWidth); + + QScopedPointer window(new QQuickView); + window->setSource(testFileUrl("hAlignWidthDependsOnImplicitWidth.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickItem *rect = window->rootObject(); + QVERIFY(rect); + + QVERIFY(rect->setProperty("horizontalAlignment", horizontalAlignment)); + QVERIFY(rect->setProperty("elide", elide)); + QVERIFY(rect->setProperty("extraWidth", extraWidth)); + + QImage image = window->grabWindow(); + const int rectX = 100 * window->screen()->devicePixelRatio(); + QCOMPARE(numberOfNonWhitePixels(0, rectX - 1, image), 0); + + QVERIFY(rect->setProperty("text", "this is mis-aligned")); + image = window->grabWindow(); + QCOMPARE(numberOfNonWhitePixels(0, rectX - 1, image), 0); +} + QTEST_MAIN(tst_qquicktext) #include "tst_qquicktext.moc" -- cgit v1.2.3