From 5a9fd4f49ec48c91c70699fc40af8f843c51b4ab Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 28 Feb 2017 15:55:39 +0100 Subject: [PATCH 01/26] V4 Debugger: Don't crash when stepping to the end of a script The last instruction is a return, which leads to an invalid context. Don't try to save that context, but rather clear the current one. Change-Id: I468b7420c4ca0842209c9b00478f99cc4dc69726 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qv4debugger.cpp | 7 ++++- .../debugger/qv4debugger/tst_qv4debugger.cpp | 30 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 5cc2043cb1..75cbc9eba1 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -230,7 +230,12 @@ void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal) QMutexLocker locker(&m_lock); if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { - m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); + if (QV4::ExecutionContext *parentContext + = m_engine->parentContext(m_engine->currentContext)) { + m_currentContext.set(m_engine, *parentContext); + } else { + m_currentContext.clear(); + } m_stepping = StepOver; m_returnedValue.set(m_engine, retVal); } diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 6793596174..7668c8b016 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -176,6 +176,7 @@ public: , m_captureContextInfo(false) , m_thrownValue(-1) , collector(engine) + , m_resumeSpeed(QV4Debugger::FullThrottle) , m_debugger(0) { } @@ -214,7 +215,7 @@ public slots: if (m_captureContextInfo) captureContextInfo(debugger); - debugger->resume(QV4Debugger::FullThrottle); + debugger->resume(m_resumeSpeed); } public: @@ -280,6 +281,7 @@ public: int context; }; QVector m_expressionRequests; + QV4Debugger::Speed m_resumeSpeed; QList m_expressionResults; QList m_expressionRefs; QV4Debugger *m_debugger; @@ -324,6 +326,7 @@ private slots: void breakInWith(); void evaluateExpression(); + void stepToEndOfScript(); private: QV4Debugger *debugger() const @@ -758,6 +761,31 @@ void tst_qv4debugger::evaluateExpression() } } +void tst_qv4debugger::stepToEndOfScript() +{ + QString script = + "var ret = 0;\n" + "ret += 4;\n" + "ret += 1;\n" + "ret += 5;\n"; + + debugger()->addBreakPoint("toEnd", 1); + m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver; + evaluateJavaScript(script, "toEnd"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step); + QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 5); + for (int i = 0; i < 4; ++i) { + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(i); + QCOMPARE(state.fileName, QString("toEnd")); + QCOMPARE(state.lineNumber, i + 1); + } + + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(4); + QCOMPARE(state.fileName, QString("toEnd")); + QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number. +} + QTEST_MAIN(tst_qv4debugger) #include "tst_qv4debugger.moc" From c67d33db5b4e78027181be492d4757ac786e5c1f Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 28 Feb 2017 15:07:42 +0100 Subject: [PATCH 02/26] Add a source location to the final Jump in a for loop Otherwise it will assume the last statement as the location of the jump, and that might be a statement that is never hit. Task-number: QTBUG-59204 Change-Id: I66019a284b061358939b23e649ca0832b5442388 Reviewed-by: hjk Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 2 +- .../debugger/qv4debugger/tst_qv4debugger.cpp | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 8d0126ebb3..819f4615f2 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2499,7 +2499,7 @@ bool Codegen::visit(LocalForStatement *ast) _block = forbody; statement(ast->statement); - _block->JUMP(forstep); + setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); _block = forstep; statement(ast->expression); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 7668c8b016..8362cb9ed6 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -327,6 +327,7 @@ private slots: void evaluateExpression(); void stepToEndOfScript(); + void lastLineOfLoop(); private: QV4Debugger *debugger() const @@ -786,6 +787,31 @@ void tst_qv4debugger::stepToEndOfScript() QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number. } +void tst_qv4debugger::lastLineOfLoop() +{ + QString script = + "var ret = 0;\n" + "for (var i = 0; i < 10; ++i) {\n" + " if (i == 2)\n" + " ret += 4;\n" // breakpoint, then step over + " else \n" + " ret += 1;\n" + "}\n"; // after step over + + debugger()->addBreakPoint("trueBranch", 4); + m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver; + evaluateJavaScript(script, "trueBranch"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step); + QVERIFY(m_debuggerAgent->m_statesWhenPaused.count() > 1); + QV4Debugger::ExecutionState firstState = m_debuggerAgent->m_statesWhenPaused.first(); + QCOMPARE(firstState.fileName, QString("trueBranch")); + QCOMPARE(firstState.lineNumber, 4); + QV4Debugger::ExecutionState secondState = m_debuggerAgent->m_statesWhenPaused.at(1); + QCOMPARE(secondState.fileName, QString("trueBranch")); + QCOMPARE(secondState.lineNumber, 7); +} + QTEST_MAIN(tst_qv4debugger) #include "tst_qv4debugger.moc" From 9bd2705a2f02d1c54f179aa096142c151046c642 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 27 Feb 2017 13:50:32 +0100 Subject: [PATCH 03/26] OpenVG: Fix glyph cache to actually ref count In my haste to add the code to cleanup unused glyphs I neglected to add the coded needed to ref count the glyphs. So glyphs that were being used more than once were getting removed too early. Change-Id: If3fa083313c345beac6705956067bee0932f7f60 Reviewed-by: Laszlo Agocs --- .../openvg/qsgopenvgfontglyphcache.cpp | 25 +++++++++++-------- .../openvg/qsgopenvgfontglyphcache.h | 3 +-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp index dd630c776f..df47e920af 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp @@ -94,7 +94,7 @@ void QSGOpenVGFontGlyphCache::populate(const QVector &glyphs) referencedGlyphs.insert(glyphIndex); - if (!m_cachedGlyphs.contains(glyphIndex)) { + if (!m_glyphReferences.contains(glyphIndex)) { newGlyphs.insert(glyphIndex); } } @@ -119,17 +119,9 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet &glyphs) { VGfloat origin[2]; VGfloat escapement[2]; - QRectF metrics; QRawFont rawFont = m_referenceFont; - // Before adding any new glyphs, remove any unused glyphs - for (auto glyph : qAsConst(m_unusedGlyphs)) { - vgClearGlyph(m_font, glyph); - } - for (auto glyph : glyphs) { - m_cachedGlyphs.insert(glyph); - // Calculate the path for the glyph and cache it. QPainterPath path = rawFont.pathForGlyph(glyph); VGPath vgPath; @@ -151,12 +143,23 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet &glyphs) void QSGOpenVGFontGlyphCache::referenceGlyphs(const QSet &glyphs) { - m_unusedGlyphs -= glyphs; + for (auto glyph : glyphs) { + if (m_glyphReferences.contains(glyph)) + m_glyphReferences[glyph] += 1; + else + m_glyphReferences.insert(glyph, 1); + } } void QSGOpenVGFontGlyphCache::releaseGlyphs(const QSet &glyphs) { - m_unusedGlyphs += glyphs; + for (auto glyph : glyphs) { + int references = m_glyphReferences[glyph] -= 1; + if (references == 0) { + vgClearGlyph(m_font, glyph); + m_glyphReferences.remove(glyph); + } + } } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h index a88d28b0fe..107ec0c892 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h @@ -87,8 +87,7 @@ private: int m_glyphCount; VGFont m_font; - QSet m_cachedGlyphs; - QSet m_unusedGlyphs; + QHash m_glyphReferences; }; From 55fec6b2db17db43fc7d03ef5d482cc67b16543b Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 27 Feb 2017 16:45:44 +0100 Subject: [PATCH 04/26] Software: Flush whole window on Expose event The software render loop updates the window content via either an expose event or an update event. An expose event should always flush the entire backingstore, but previously only the newly updated region would be flushed. The first time a window is exposed this is fine because the whole scene would be rendered, but when hidden and shown again, the software renderer might show that nothing has changed, and nothing would be flushed. A new flag has been added that forces a full window flush for an expose event. Task-number: QTBUG-59177 Change-Id: I3700bbcc76bc97be4eb0c822e2945ebef339f11a Reviewed-by: Laszlo Agocs --- .../adaptations/software/qsgsoftwarerenderloop.cpp | 9 ++++++--- .../adaptations/software/qsgsoftwarerenderloop_p.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 6856d34616..b3b8274a73 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -101,7 +101,7 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) } } -void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) +void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); if (!m_windows.contains(window)) @@ -174,7 +174,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) if (alsoSwap && window->isVisible()) { //Flush backingstore to window - m_backingStores[window]->flush(softwareRenderer->flushRegion()); + if (!isNewExpose) + m_backingStores[window]->flush(softwareRenderer->flushRegion()); + else + m_backingStores[window]->flush(QRegion(QRect(QPoint(0,0), window->size()))); cd->fireFrameSwapped(); } @@ -206,7 +209,7 @@ void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window) { if (window->isExposed()) { m_windows[window].updatePending = true; - renderWindow(window); + renderWindow(window, true); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h index 02dcf4eefa..c724d18298 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h @@ -69,7 +69,7 @@ public: void windowDestroyed(QQuickWindow *window) override; - void renderWindow(QQuickWindow *window); + void renderWindow(QQuickWindow *window, bool isNewExpose = false); void exposureChanged(QQuickWindow *window) override; QImage grab(QQuickWindow *window) override; From 59c94123e3661274d14f69c3df71fae9325b7eea Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 28 Feb 2017 15:39:25 +0100 Subject: [PATCH 05/26] Fix memory leak in QQDMIncubationTask::statusChanged Mimic the same process we do in QQmlDelegateModelPrivate::incubatorStatusChanged i.e. there's no need to check for object before destroying contextData Change-Id: I8cb1cda6da43bada350183ae11ee9b85b7fffee5 Reviewed-by: Simon Hausmann --- src/qml/types/qqmldelegatemodel.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index a5878dcffd..34bc266cb5 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -837,10 +837,9 @@ void QQDMIncubationTask::statusChanged(Status status) } else if (isDoneIncubating(status)) { Q_ASSERT(incubating); // The model was deleted from under our feet, cleanup ourselves - if (incubating->object) { - delete incubating->object; - - incubating->object = 0; + delete incubating->object; + incubating->object = 0; + if (incubating->contextData) { incubating->contextData->destroy(); incubating->contextData = 0; } From 71a2dbed467be0041b6793dda868655b2632e830 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 5 Jan 2017 16:16:43 +0100 Subject: [PATCH 06/26] When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 9 ++- .../quick/qquickmultipointtoucharea/BLACKLIST | 2 - .../tst_qquickmultipointtoucharea.cpp | 2 +- .../auto/quick/touchmouse/tst_touchmouse.cpp | 76 ++++++++++++++++++- 4 files changed, 80 insertions(+), 9 deletions(-) delete mode 100644 tests/auto/quick/qquickmultipointtoucharea/BLACKLIST diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 4dc8cd0a37..340683f6c3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2593,9 +2593,14 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - touchMouseId = tp.id(); touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); + if (touchMouseId == -1) { + // the point was grabbed as a pure touch point before, now it will be treated as mouse + // but the old receiver still needs to be informed + if (auto oldGrabber = touchMouseDevice->pointerEvent()->pointById(tp.id())->grabber()) + oldGrabber->touchUngrabEvent(); + } + touchMouseId = tp.id(); target->grabMouse(); } filtered = true; diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST deleted file mode 100644 index 1777af9e0c..0000000000 --- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[inFlickable] -* diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 2872556a94..87acd67f6a 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -800,7 +800,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QVERIFY(flickable->contentY() < 0); QVERIFY(flickable->isMoving()); - QCOMPARE(point11->pressed(), true); + QCOMPARE(point11->pressed(), false); QTest::touchEvent(window.data(), device).release(0, p1); QQuickTouchUtils::flush(window.data()); diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 032e682137..671cefd6f5 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -71,7 +71,7 @@ Q_SIGNALS: public: EventItem(QQuickItem *parent = 0) - : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1) + : QQuickItem(parent), touchUngrabCount(0), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1) { setAcceptedMouseButtons(Qt::LeftButton); } @@ -111,11 +111,17 @@ public: eventList.append(Event(QEvent::UngrabMouse, QPoint(0,0), QPoint(0,0))); } + void touchUngrabEvent() + { + ++touchUngrabCount; + } + bool event(QEvent *event) { return QQuickItem::event(event); } QList eventList; + int touchUngrabCount; bool acceptMouse; bool acceptTouch; bool filterTouch; // when used as event filter @@ -158,6 +164,7 @@ private slots: void mouseOverTouch(); void buttonOnFlickable(); + void touchButtonOnFlickable(); void buttonOnDelayedPressFlickable_data(); void buttonOnDelayedPressFlickable(); void buttonOnTouch(); @@ -568,9 +575,10 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); - p1 += QPoint(0, -10); - QPoint p2 = p1 + QPoint(0, -10); - QPoint p3 = p2 + QPoint(0, -10); + int dragDelta = -qApp->styleHints()->startDragDistance(); + p1 += QPoint(0, dragDelta); + QPoint p2 = p1 + QPoint(0, dragDelta); + QPoint p3 = p2 + QPoint(0, dragDelta); QQuickTouchUtils::flush(window.data()); QTest::touchEvent(window.data(), device).move(0, p1, window.data()); QQuickTouchUtils::flush(window.data()); @@ -593,6 +601,66 @@ void tst_TouchMouse::buttonOnFlickable() QQuickTouchUtils::flush(window.data()); } +void tst_TouchMouse::touchButtonOnFlickable() +{ + // flickable - height 500 / 1000 + // - eventItem1 y: 100, height 100 + // - eventItem2 y: 300, height 100 + + QScopedPointer window(createView()); + window->setSource(testFileUrl("buttononflickable.qml")); + window->show(); + QQuickViewTestUtil::centerOnScreen(window.data()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); + + QQuickFlickable *flickable = window->rootObject()->findChild("flickable"); + QVERIFY(flickable); + + EventItem *eventItem2 = window->rootObject()->findChild("eventItem2"); + QVERIFY(eventItem2); + QCOMPARE(eventItem2->eventList.size(), 0); + eventItem2->acceptTouch = true; + + // press via touch, then drag: check that flickable moves and that the button gets ungrabbed + QCOMPARE(eventItem2->eventList.size(), 0); + QPoint p1 = QPoint(10, 310); + QTest::touchEvent(window.data(), device).press(0, p1, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(eventItem2->eventList.size(), 1); + QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); + + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); + QVERIFY(windowPriv->touchMouseId == -1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabber(), eventItem2); + QCOMPARE(window->mouseGrabberItem(), nullptr); + + int dragDelta = qApp->styleHints()->startDragDistance() * -0.7; + p1 += QPoint(0, dragDelta); + QPoint p2 = p1 + QPoint(0, dragDelta); + QPoint p3 = p2 + QPoint(0, dragDelta); + + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p1, window.data()); + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p2, window.data()); + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p3, window.data()); + QQuickTouchUtils::flush(window.data()); + + QVERIFY(eventItem2->eventList.size() > 2); + QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); + QCOMPARE(eventItem2->touchUngrabCount, 1); + QCOMPARE(window->mouseGrabberItem(), flickable); + QVERIFY(windowPriv->touchMouseId != -1); + QCOMPARE(pointerEvent->point(0)->grabber(), flickable); + QVERIFY(flickable->isMovingVertically()); + + QTest::touchEvent(window.data(), device).release(0, p3, window.data()); + QQuickTouchUtils::flush(window.data()); +} + void tst_TouchMouse::buttonOnDelayedPressFlickable_data() { QTest::addColumn("scrollBeforeDelayIsOver"); From 60708ee6227df017269146d2c0cfd4238ec25b9f Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Thu, 2 Mar 2017 12:10:00 +0100 Subject: [PATCH 07/26] Fix build in clang5.0 Fixes: X86Assembler.h:281:19: error: 'm_assembler' is a protected member of 'JSC::AbstractMacroAssembler' masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); AbstractMacroAssembler.h:819:19: note: declared protected here AssemblerType m_assembler; Change-Id: I04f6dc254e9826b9835a43b604a05ea4c57f661b Reviewed-by: Simon Hausmann --- src/3rdparty/masm/assembler/X86Assembler.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h index 24462ef38f..46f2cd714a 100644 --- a/src/3rdparty/masm/assembler/X86Assembler.h +++ b/src/3rdparty/masm/assembler/X86Assembler.h @@ -253,6 +253,7 @@ public: { } +#if defined(V4_BOOTSTRAP) template class Jump { template @@ -291,6 +292,7 @@ public: private: AssemblerLabel m_label; }; +#endif // Stack operations: From 201d9f1c62516542d4558f0d2b07542e1082b497 Mon Sep 17 00:00:00 2001 From: Kimmo Ollila Date: Fri, 3 Mar 2017 00:59:47 +0200 Subject: [PATCH 08/26] Allow import static plugins even when library feature is disabled Removing QT_CONFIG(library) checks around static plugin handling code Change-Id: I5408d0fee2f58b27372c59004351f37ee8f566b9 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlimport.cpp | 39 +++++--------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index c07d5c740a..ee5b38717b 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -210,7 +210,6 @@ QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringR } // namespace -#if QT_CONFIG(library) struct RegisteredPlugin { QString uri; QPluginLoader* loader; @@ -221,21 +220,23 @@ struct StringRegisteredPluginMap : public QMap { }; Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders + void qmlClearEnginePlugins() { StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); QMutexLocker lock(&plugins->mutex); +#if QT_CONFIG(library) for (auto &plugin : qAsConst(*plugins)) { QPluginLoader* loader = plugin.loader; if (loader && !loader->unload()) qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString())); delete loader; } +#endif plugins->clear(); } typedef QPair StaticPluginPair; -#endif /*! \internal @@ -332,10 +333,9 @@ public: const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList *errors, bool lowPrecedence = false); -#if QT_CONFIG(library) - bool populatePluginPairVector(QVector &result, const QString &uri, const QStringList &versionUris, + + bool populatePluginPairVector(QVector &result, const QString &uri, const QStringList &versionUris, const QString &qmldirPath, QList *errors); -#endif }; /*! @@ -959,7 +959,6 @@ static QStringList versionUriList(const QString &uri, int vmaj, int vmin) return result; } -#if QT_CONFIG(library) static QVector makePlugins() { QVector plugins; @@ -1009,7 +1008,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector &res } return true; } -#endif #if defined(QT_SHARED) || !QT_CONFIG(library) static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) @@ -1030,7 +1028,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QQmlTypeLoaderQmldirContent *qmldir, QList *errors) { -#if QT_CONFIG(library) Q_ASSERT(qmldir); if (qmlImportTrace()) @@ -1143,22 +1140,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath); } - -#else - Q_UNUSED(vmaj); - Q_UNUSED(vmin); - Q_UNUSED(database); - Q_UNUSED(qmldir); - - if (errors) { - QQmlError error; - error.setDescription(msgCannotLoadPlugin(uri, QQmlImportDatabase::tr("library loading is disabled"))); - error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); - errors->prepend(error); - } - - return false; -#endif // library return true; } @@ -2014,7 +1995,6 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &basePath, const QString &uri, const QString &typeNamespace, int vmaj, QList *errors) { -#if QT_CONFIG(library) // Dynamic plugins are differentiated by their filepath. For static plugins we // don't have that information so we use their address as key instead. const QString uniquePluginID = QString::asprintf("%p", instance); @@ -2050,15 +2030,6 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba } return true; -#else - Q_UNUSED(instance); - Q_UNUSED(basePath); - Q_UNUSED(uri); - Q_UNUSED(typeNamespace); - Q_UNUSED(vmaj); - Q_UNUSED(errors); - return false; -#endif } /*! From 05a88efb266ec3b7b16d6db0d971c21ae7f45c9d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 27 Feb 2017 15:17:35 +0100 Subject: [PATCH 09/26] QQuickAnimatorProxyJob: make sure to stop when detached from a window Qt Quick Controls 2 StackView auto tests exposed an issue that animator proxy jobs keep ticking after being unassociated from a window. Change-Id: Ib9b3a0e02ac4cc3f3e98ddf05c8b01f0bbd614d3 Reviewed-by: Gunnar Sletta --- src/quick/util/qquickanimatorjob.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 4aacb09c97..dced8b49a9 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -140,6 +140,11 @@ QObject *QQuickAnimatorProxyJob::findAnimationContext(QQuickAbstractAnimation *a void QQuickAnimatorProxyJob::updateCurrentTime(int) { + // A proxy which is being ticked should be associated with a window, (see + // setWindow() below). If we get here when there is no more controller we + // have a problem. + Q_ASSERT(m_controller); + // We do a simple check here to see if the animator has run and stopped on // the render thread. isPendingStart() will perform a check against jobs // that have been scheduled for start, but that will not yet have entered @@ -167,9 +172,9 @@ void QQuickAnimatorProxyJob::updateState(QAbstractAnimationJob::State newState, } } else if (newState == Stopped) { - syncBackCurrentValues(); m_internalState = State_Stopped; if (m_controller) { + syncBackCurrentValues(); m_controller->cancel(m_job); } } @@ -193,6 +198,7 @@ void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window) if (m_job && m_controller) m_controller->cancel(m_job); m_controller = nullptr; + stop(); } else if (!m_controller && m_job) { m_controller = QQuickWindowPrivate::get(window)->animationController; From c6917f27cbb26c140d6e514ca8a268bbf41f49a2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Mar 2017 10:29:47 +0100 Subject: [PATCH 10/26] Fix restoring IR from cache files Commit eeb08d9537d0b4e77b91848169e0bb79ec3d912c was missing the code to restore the aliases, the AST function formals as well as the object flags/id. This also fixes crashes with the QML compiler. Change-Id: I1c50816dc77ae66edbc3ddc7146da4edfd4170f4 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 8a7507c92e..0a6efa7867 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -2116,7 +2116,8 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - + object->flags = serializedObject->flags; + object->id = serializedObject->id; object->location = serializedObject->location; object->locationOfIdProperty = serializedObject->locationOfIdProperty; @@ -2175,6 +2176,15 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO object->properties->append(p); } + { + const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); + for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { + QmlIR::Alias *a = pool->New(); + *static_cast(a) = *serializedAlias; + object->aliases->append(a); + } + } + QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); @@ -2205,6 +2215,11 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO const QString name = unit->stringAt(compiledFunction->nameIndex); f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0); + f->formals.allocate(pool, int(compiledFunction->nFormals)); + formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) + f->formals[i] = *formalNameIdx; + object->functions->append(f); } From 38919dc8259b5faccf2d755cb62f668047369565 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 2 Mar 2017 16:55:09 +0100 Subject: [PATCH 11/26] Fix qml cache generation for non-prefix builds Generate cache files right in the target locatioa, similar to how qml_module.prf uses COPIES to copy .qml source files to the target location in non-prefix builds. Change-Id: I06f6112c29ffd212e29a84e29418d042255b5861 Reviewed-by: Lars Knoll --- tools/qmlcachegen/qmlcache.prf | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tools/qmlcachegen/qmlcache.prf b/tools/qmlcachegen/qmlcache.prf index fed9f0d2f3..e0c62ec4d3 100644 --- a/tools/qmlcachegen/qmlcache.prf +++ b/tools/qmlcachegen/qmlcache.prf @@ -1,12 +1,26 @@ qtPrepareTool(QML_CACHEGEN, qmlcachegen) +isEmpty(TARGETPATH): error("Must set TARGETPATH (QML import name) for ahead-of-time QML cache generation") + !isEmpty(QT_TARGET_ARCH):QML_CACHEGEN_ARCH=$$QT_TARGET_ARCH else:QML_CACHEGEN_ARCH=$$QT_ARCH -qmlcachegen.input = QML_FILES -qmlcachegen.output = ${QMAKE_FILE_IN}c -qmlcachegen.commands = $$QML_CACHEGEN --target-architecture=$$QML_CACHEGEN_ARCH ${QMAKE_FILE_IN} +CACHEGEN_FILES= +for(qmlf, QML_FILES) { + contains(qmlf,.*\\.js$)|contains(qmlf,.*\\.qml$) { + CACHEGEN_FILES += $$qmlf + } +} + +qmlcachegen.input = CACHEGEN_FILES +prefix_build { + qmlcachegen.output = ${QMAKE_FILE_IN}c +} else { + qmlcachegen.output = $$[QT_INSTALL_QML]/$$TARGETPATH/${QMAKE_FILE_IN}c + qmlcachegen.CONFIG = no_link target_predeps +} +qmlcachegen.commands = $$QML_CACHEGEN --target-architecture=$$QML_CACHEGEN_ARCH -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN} -qmlcachegen.variable_out = AUX_QML_FILES +qmlcachegen.variable_out = GENERATED_FILES QMAKE_EXTRA_COMPILERS += qmlcachegen From 1102f6ca7f1bb09e77f100b7e2afcac9f13e322c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 2 Mar 2017 15:52:38 +0100 Subject: [PATCH 12/26] Fix qmlcachegen command line parameters Add support for specifying the output file name Change-Id: I3ec3cecae2334a7640baa928c0739c5521496d2d Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 13 ++++--------- src/qml/compiler/qv4compileddata_p.h | 2 +- tools/qmlcachegen/qmlcachegen.cpp | 22 +++++++++++++++------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a8f065210b..dba04a7187 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -77,13 +77,7 @@ namespace QV4 { namespace CompiledData { -#ifdef V4_BOOTSTRAP -static QString cacheFilePath(const QString &localSourcePath) -{ - const QString localCachePath = localSourcePath + QLatin1Char('c'); - return localCachePath; -} -#else +#if !defined(V4_BOOTSTRAP) static QString cacheFilePath(const QUrl &url) { const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); @@ -408,7 +402,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString) #endif // V4_BOOTSTRAP #if defined(V4_BOOTSTRAP) -bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) #else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) #endif @@ -425,11 +419,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) *errorString = QStringLiteral("File has to be a local file."); return false; } + const QString outputFileName = cacheFilePath(unitUrl); #endif #if QT_CONFIG(temporaryfile) // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(cacheFilePath(unitUrl)); + QSaveFile cacheFile(outputFileName); if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = cacheFile.errorString(); return false; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 13a0c4b075..8c7d0d7e19 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -908,7 +908,7 @@ protected: public: #if defined(V4_BOOTSTRAP) - bool saveToDisk(const QString &unitUrl, QString *errorString); + bool saveToDisk(const QString &outputFileName, QString *errorString); #else bool saveToDisk(const QUrl &unitUrl, QString *errorString); #endif diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 977c5b6ff1..c0faabe393 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -80,7 +80,7 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic return message; } -static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error) +static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); @@ -153,7 +153,7 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation; irDocument.javaScriptCompilationUnit->data = unit; - if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message)) + if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) return false; free(unit); @@ -161,7 +161,7 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i return true; } -static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error) +static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); @@ -233,7 +233,8 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is // ### translation binding simplification - QScopedPointer isel(iselFactory->create(/*engine*/nullptr, /*executable allocator*/nullptr, &irDocument.jsModule, &irDocument.jsGenerator)); + QV4::ExecutableAllocator allocator; + QScopedPointer isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator)); // Disable lookups in non-standalone (aka QML) mode isel->setUseFastLookups(false); irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false); @@ -243,7 +244,7 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is unit->flags |= QV4::CompiledData::Unit::StaticData; irDocument.javaScriptCompilationUnit->data = unit; - if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message)) { + if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) { engine->setDirectives(oldDirs); return false; } @@ -270,6 +271,9 @@ int main(int argc, char **argv) QCommandLineOption targetArchitectureOption(QStringLiteral("target-architecture"), QCoreApplication::translate("main", "Target architecture"), QCoreApplication::translate("main", "architecture")); parser.addOption(targetArchitectureOption); + QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name")); + parser.addOption(outputFileOption); + parser.addPositionalArgument(QStringLiteral("[qml file]"), QStringLiteral("QML source file to generate cache for.")); @@ -294,13 +298,17 @@ int main(int argc, char **argv) Error error; + QString outputFileName = inputFile + QLatin1Char('c'); + if (parser.isSet(outputFileOption)) + outputFileName = parser.value(outputFileOption); + if (inputFile.endsWith(QLatin1String(".qml"))) { - if (!compileQmlFile(inputFile, isel.data(), &error)) { + if (!compileQmlFile(inputFile, outputFileName, isel.data(), &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } } else if (inputFile.endsWith(QLatin1String(".js"))) { - if (!compileJSFile(inputFile, isel.data(), &error)) { + if (!compileJSFile(inputFile, outputFileName, isel.data(), &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } From ba6de61acd3129ad1435f6bca7f564385212f95c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 3 Mar 2017 10:35:13 +0100 Subject: [PATCH 13/26] Fix skipping of qml cache generation on unsupported architectures Detect support for the target architecture at qmake time and gently skip the process. Change-Id: I7cc22a0cfb9a8b503c182062a56e506035f84fa2 Reviewed-by: Lars Knoll --- tools/qmlcachegen/qmlcache.prf | 16 ++++++++++++++-- tools/qmlcachegen/qmlcachegen.cpp | 26 +++++++++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/tools/qmlcachegen/qmlcache.prf b/tools/qmlcachegen/qmlcache.prf index e0c62ec4d3..5cac5592c3 100644 --- a/tools/qmlcachegen/qmlcache.prf +++ b/tools/qmlcachegen/qmlcache.prf @@ -1,10 +1,22 @@ -qtPrepareTool(QML_CACHEGEN, qmlcachegen) +static { + message("QML cache generation ahead of time is not supported in static builds") + return() +} + +qtPrepareTool(QML_CACHEGEN, qmlcachegen, _ARCH_CHECK) isEmpty(TARGETPATH): error("Must set TARGETPATH (QML import name) for ahead-of-time QML cache generation") !isEmpty(QT_TARGET_ARCH):QML_CACHEGEN_ARCH=$$QT_TARGET_ARCH else:QML_CACHEGEN_ARCH=$$QT_ARCH +QML_CACHEGEN_ARGS=--target-architecture=$$QML_CACHEGEN_ARCH + +!system($$QML_CACHEGEN_ARCH_CHECK $$QML_CACHEGEN_ARGS --check-if-supported) { + message("QML cache generation requested but target architecture $$QML_CACHEGEN_ARCH is not supported.") + return() +} + CACHEGEN_FILES= for(qmlf, QML_FILES) { contains(qmlf,.*\\.js$)|contains(qmlf,.*\\.qml$) { @@ -19,7 +31,7 @@ prefix_build { qmlcachegen.output = $$[QT_INSTALL_QML]/$$TARGETPATH/${QMAKE_FILE_IN}c qmlcachegen.CONFIG = no_link target_predeps } -qmlcachegen.commands = $$QML_CACHEGEN --target-architecture=$$QML_CACHEGEN_ARCH -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} +qmlcachegen.commands = $$QML_CACHEGEN $$QML_CACHEGEN_ARGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN} qmlcachegen.variable_out = GENERATED_FILES diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index c0faabe393..fa75d14c8e 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -274,11 +274,32 @@ int main(int argc, char **argv) QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name")); parser.addOption(outputFileOption); + QCommandLineOption checkIfSupportedOption(QStringLiteral("check-if-supported"), QCoreApplication::translate("main", "Check if cache generate is supported on the specified target architecture")); + parser.addOption(checkIfSupportedOption); + parser.addPositionalArgument(QStringLiteral("[qml file]"), QStringLiteral("QML source file to generate cache for.")); parser.process(app); + if (!parser.isSet(targetArchitectureOption)) { + fprintf(stderr, "Target architecture not specified. Please specify with --target-architecture=\n"); + parser.showHelp(); + return EXIT_FAILURE; + } + + QScopedPointer isel; + const QString targetArchitecture = parser.value(targetArchitectureOption); + + isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture)); + + if (parser.isSet(checkIfSupportedOption)) { + if (isel.isNull()) + return EXIT_FAILURE; + else + return EXIT_SUCCESS; + } + const QStringList sources = parser.positionalArguments(); if (sources.isEmpty()){ parser.showHelp(); @@ -288,11 +309,6 @@ int main(int argc, char **argv) } const QString inputFile = sources.first(); - QScopedPointer isel; - const QString targetArchitecture = parser.value(targetArchitectureOption); - - isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture)); - if (!isel) isel.reset(new QV4::Moth::ISelFactory); From a54f08e1ead70e821f438279e548d54e38c4aa8f Mon Sep 17 00:00:00 2001 From: Michael Winkelmann Date: Thu, 2 Mar 2017 17:16:09 +0100 Subject: [PATCH 14/26] Replace QList with QVector and function pointers The usage of the QQmlListProperty QList constructor is discouraged since QList violates QML's memory management rules. Replaced the QList with a QVector and passed the function pointers to the QQmlListProperty constructor instead, as officially recommended. Change-Id: I6d28a43530cc3edd5e7d89c351bad70deb721689 Reviewed-by: Simon Hausmann --- .../properties/birthdayparty.cpp | 31 ++++++++++++++++++- .../properties/birthdayparty.h | 10 +++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/examples/qml/referenceexamples/properties/birthdayparty.cpp b/examples/qml/referenceexamples/properties/birthdayparty.cpp index b69b7c8a11..0b426fc00b 100644 --- a/examples/qml/referenceexamples/properties/birthdayparty.cpp +++ b/examples/qml/referenceexamples/properties/birthdayparty.cpp @@ -57,9 +57,18 @@ void BirthdayParty::setHost(Person *c) QQmlListProperty BirthdayParty::guests() { - return QQmlListProperty(this, m_guests); + return QQmlListProperty(this, this, + &BirthdayParty::appendGuest, + &BirthdayParty::guestCount, + &BirthdayParty::guest, + &BirthdayParty::clearGuests); } +void BirthdayParty::appendGuest(Person* p) { + m_guests.append(p); +} + + int BirthdayParty::guestCount() const { return m_guests.count(); @@ -69,5 +78,25 @@ Person *BirthdayParty::guest(int index) const { return m_guests.at(index); } + +void BirthdayParty::clearGuests() { + return m_guests.clear(); +} + // ![0] +void BirthdayParty::appendGuest(QQmlListProperty* list, Person* p) { + reinterpret_cast< BirthdayParty* >(list->data)->appendGuest(p); +} + +void BirthdayParty::clearGuests(QQmlListProperty* list) { + reinterpret_cast< BirthdayParty* >(list->data)->clearGuests(); +} + +Person* BirthdayParty::guest(QQmlListProperty* list, int i) { + return reinterpret_cast< BirthdayParty* >(list->data)->guest(i); +} + +int BirthdayParty::guestCount(QQmlListProperty* list) { + return reinterpret_cast< BirthdayParty* >(list->data)->guestCount(); +} diff --git a/examples/qml/referenceexamples/properties/birthdayparty.h b/examples/qml/referenceexamples/properties/birthdayparty.h index d0a2cad285..df55df3e80 100644 --- a/examples/qml/referenceexamples/properties/birthdayparty.h +++ b/examples/qml/referenceexamples/properties/birthdayparty.h @@ -41,6 +41,7 @@ #define BIRTHDAYPARTY_H #include +#include #include #include "person.h" @@ -63,12 +64,19 @@ public: void setHost(Person *); QQmlListProperty guests(); + void appendGuest(Person*); int guestCount() const; Person *guest(int) const; + void clearGuests(); private: + static void appendGuest(QQmlListProperty*, Person*); + static int guestCount(QQmlListProperty*); + static Person* guest(QQmlListProperty*, int); + static void clearGuests(QQmlListProperty*); + Person *m_host; - QList m_guests; + QVector m_guests; }; // ![3] From 7fe0d1a06830518e8ce6752e45a0a01b1b97fa73 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Tue, 7 Mar 2017 08:58:10 +0100 Subject: [PATCH 15/26] Revert "QQuickAnimatorProxyJob: make sure to stop when detached from a window" This reverts commit 05a88efb266ec3b7b16d6db0d971c21ae7f45c9d. It caused some test failures in qtquickcontrols and qtquickcontrols2. Task-number: QTBUG-59326 Task-number: QTBUG-59327 Change-Id: I9a26a4518bf7c106372916761aae69ba65f6df9e Reviewed-by: J-P Nurmi --- src/quick/util/qquickanimatorjob.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index dced8b49a9..4aacb09c97 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -140,11 +140,6 @@ QObject *QQuickAnimatorProxyJob::findAnimationContext(QQuickAbstractAnimation *a void QQuickAnimatorProxyJob::updateCurrentTime(int) { - // A proxy which is being ticked should be associated with a window, (see - // setWindow() below). If we get here when there is no more controller we - // have a problem. - Q_ASSERT(m_controller); - // We do a simple check here to see if the animator has run and stopped on // the render thread. isPendingStart() will perform a check against jobs // that have been scheduled for start, but that will not yet have entered @@ -172,9 +167,9 @@ void QQuickAnimatorProxyJob::updateState(QAbstractAnimationJob::State newState, } } else if (newState == Stopped) { + syncBackCurrentValues(); m_internalState = State_Stopped; if (m_controller) { - syncBackCurrentValues(); m_controller->cancel(m_job); } } @@ -198,7 +193,6 @@ void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window) if (m_job && m_controller) m_controller->cancel(m_job); m_controller = nullptr; - stop(); } else if (!m_controller && m_job) { m_controller = QQuickWindowPrivate::get(window)->animationController; From d5d12e1d6fb9aaa5a8cee7924555f0d4d19ffabc Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 1 Mar 2017 10:42:47 +0100 Subject: [PATCH 16/26] Set source location for all loop body-to-front jumps Task-number: QTBUG-59204 Change-Id: Id1a73b228cd3386c7fcc7712c2485f387238b65e Reviewed-by: hjk Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4codegen.cpp | 10 ++++---- .../debugger/qv4debugger/tst_qv4debugger.cpp | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 819f4615f2..2c1b5c57cc 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2256,7 +2256,7 @@ bool Codegen::visit(DoWhileStatement *ast) _block = loopbody; statement(ast->statement); - _block->JUMP(loopcond); + setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation()); _block = loopcond; condition(ast->expression, loopbody, loopend); @@ -2321,7 +2321,7 @@ bool Codegen::visit(ForEachStatement *ast) return false; move(*init, _block->TEMP(temp)); statement(ast->statement); - _block->JUMP(foreachin); + setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); _block = foreachin; @@ -2360,7 +2360,7 @@ bool Codegen::visit(ForStatement *ast) _block = forbody; statement(ast->statement); - _block->JUMP(forstep); + setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); _block = forstep; statement(ast->expression); @@ -2460,7 +2460,7 @@ bool Codegen::visit(LocalForEachStatement *ast) int temp = _block->newTemp(); move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); statement(ast->statement); - _block->JUMP(foreachin); + setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); _block = foreachin; @@ -2800,7 +2800,7 @@ bool Codegen::visit(WhileStatement *ast) _block = whilebody; statement(ast->statement); - _block->JUMP(whilecond); + setLocation(_block->JUMP(whilecond), ast->lastSourceLocation()); _block = whileend; leaveLoop(); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 8362cb9ed6..56320b8365 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -327,8 +327,9 @@ private slots: void evaluateExpression(); void stepToEndOfScript(); - void lastLineOfLoop(); + void lastLineOfLoop_data(); + void lastLineOfLoop(); private: QV4Debugger *debugger() const { @@ -787,16 +788,30 @@ void tst_qv4debugger::stepToEndOfScript() QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number. } +void tst_qv4debugger::lastLineOfLoop_data() +{ + QTest::addColumn("loopHead"); + QTest::addColumn("loopTail"); + + QTest::newRow("for") << "for (var i = 0; i < 10; ++i) {\n" << "}\n"; + QTest::newRow("for..in") << "for (var i in [0, 1, 2, 3, 4]) {\n" << "}\n"; + QTest::newRow("while") << "while (ret < 10) {\n" << "}\n"; + QTest::newRow("do..while") << "do {\n" << "} while (ret < 10);\n"; +} + void tst_qv4debugger::lastLineOfLoop() { + QFETCH(QString, loopHead); + QFETCH(QString, loopTail); + QString script = "var ret = 0;\n" - "for (var i = 0; i < 10; ++i) {\n" - " if (i == 2)\n" + + loopHead + + " if (ret == 2)\n" " ret += 4;\n" // breakpoint, then step over " else \n" " ret += 1;\n" - "}\n"; // after step over + + loopTail; debugger()->addBreakPoint("trueBranch", 4); m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver; From 84a07bebc94e9f92d4d596550317a89403ed291f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 6 Mar 2017 15:06:59 +0100 Subject: [PATCH 17/26] Fix crash when loading cache files generated ahead of time The offset of the runtimeStrings array differed between a V4_BOOTSTRAP build and the regular library build. This is an intermediate fix until QTBUG-58666 is fixed properly. Change-Id: Id1310ffa82f1079c1acef7730db41186fa62610f Reviewed-by: hjk Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 2 +- src/qml/compiler/qv4compileddata_p.h | 6 ++---- src/qml/debugger/qqmldebug.cpp | 10 +++++++--- tests/auto/toolsupport/tst_toolsupport.cpp | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index dba04a7187..70a03c2c1f 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -95,8 +95,8 @@ static QString cacheFilePath(const QUrl &url) #ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() : data(0) - , engine(0) , runtimeStrings(0) + , engine(0) , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 8c7d0d7e19..23139d6c8e 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -816,13 +816,11 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount // Called only when building QML, when we build the header for JS first and append QML data virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); -#ifndef V4_BOOTSTRAP - ExecutionEngine *engine; -#endif - QV4::Heap::String **runtimeStrings; // Array #ifndef V4_BOOTSTRAP + ExecutionEngine *engine; + QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 15230d75a5..681dc06215 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -42,6 +42,7 @@ #include "qqmldebugserviceinterfaces_p.h" #include +#include QT_BEGIN_NAMESPACE @@ -181,12 +182,12 @@ bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName, return connector ? connector->open(configuration) : false; } -enum { HookCount = 3 }; +enum { HookCount = 4 }; // 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, + 2, // Number of entries in this array. HookCount, @@ -194,7 +195,10 @@ 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. - 2 + 3, + + // Version of the cache data. + QV4_DATA_STRUCTURE_VERSION }; Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0])); diff --git a/tests/auto/toolsupport/tst_toolsupport.cpp b/tests/auto/toolsupport/tst_toolsupport.cpp index 9a11a67e65..526ba8f375 100644 --- a/tests/auto/toolsupport/tst_toolsupport.cpp +++ b/tests/auto/toolsupport/tst_toolsupport.cpp @@ -109,7 +109,7 @@ void tst_toolsupport::offsets_data() = QTest::newRow("CompiledData::CompilationUnit::runtimeStrings") << pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::runtimeStrings); - data << 16 << 32; + data << 12 << 24; } { From 1ecefb43f250374a598bc64e8ff3d3af2aac006c Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 1 Mar 2017 10:10:05 +0100 Subject: [PATCH 18/26] Doc: Remove mentioning of non-existing setContents method Change-Id: I6c8c27390b6309e0d2bd0c758d04f864fce51069 Reviewed-by: Leena Miettinen --- src/quickwidgets/qquickwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 4ed41a0c6c..ea02723db8 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1140,7 +1140,7 @@ QSize QQuickWidget::initialSize() const /*! Returns the view's root \l {QQuickItem} {item}. Can be null - when setContents/setSource has not been called, if they were called with + when setSource() has not been called, if it was called with broken QtQuick code or while the QtQuick contents are being created. */ QQuickItem *QQuickWidget::rootObject() const From 25555238cde42daf5a73669234e737c90ef9ea5f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 21 Jan 2016 11:40:18 +0100 Subject: [PATCH 19/26] Fix copied QDragMoveEvent drop action to propagate to original event Trying to set the drop action and the accepted state in a overridden dragMoveEvent handler, does not get propagated to the original QDragMoveEvent, because the event passed to the handler is a copy. This does not allow canceling the drop action in the move handler, or change the proposed action to a different one. Changing these values in the move handler is important to allow modifying the cursor when moving / hovering above a possible drop item, depending on user conditions. Fix consists in copying the drop action and accepted values to the original event, as well as removing the hard-coded setAccepted (true) call. Task-number: QTBUG-58260 Change-Id: I7a4bd4e68ee1023a36a63d3e835c282077e4187c Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 +- .../quick/qquickwindow/tst_qquickwindow.cpp | 258 ++++++++++++++++++ 2 files changed, 261 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 340683f6c3..e6245f90f3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2397,7 +2397,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast(event); if (deliverDragEvent(grabber, **grabItem, moveEvent)) { - moveEvent->setAccepted(true); for (++grabItem; grabItem != grabber->end();) { QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); if ((**grabItem)->contains(p)) { @@ -2472,7 +2471,10 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); + translatedEvent.setAccepted(event->isAccepted()); QCoreApplication::sendEvent(item, &translatedEvent); + event->setAccepted(translatedEvent.isAccepted()); + event->setDropAction(translatedEvent.dropAction()); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index dd00154935..1d6547c5be 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -372,6 +373,8 @@ private slots: void grabContentItemToImage(); + void testDragEventPropertyPropagation(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -2573,6 +2576,261 @@ void tst_qquickwindow::grabContentItemToImage() QTRY_COMPARE(created->property("success").toInt(), 1); } +class TestDropTarget : public QQuickItem +{ + Q_OBJECT +public: + TestDropTarget(QQuickItem *parent = 0) + : QQuickItem(parent) + , enterDropAction(Qt::CopyAction) + , moveDropAction(Qt::CopyAction) + , dropDropAction(Qt::CopyAction) + , enterAccept(true) + , moveAccept(true) + , dropAccept(true) + { + setFlags(ItemAcceptsDrops); + } + + void reset() + { + enterDropAction = Qt::CopyAction; + moveDropAction = Qt::CopyAction; + dropDropAction = Qt::CopyAction; + enterAccept = true; + moveAccept = true; + dropAccept = true; + } + + void dragEnterEvent(QDragEnterEvent *event) + { + event->setAccepted(enterAccept); + event->setDropAction(enterDropAction); + } + + void dragMoveEvent(QDragMoveEvent *event) + { + event->setAccepted(moveAccept); + event->setDropAction(moveDropAction); + } + + void dropEvent(QDropEvent *event) + { + event->setAccepted(dropAccept); + event->setDropAction(dropDropAction); + } + + Qt::DropAction enterDropAction; + Qt::DropAction moveDropAction; + Qt::DropAction dropDropAction; + bool enterAccept; + bool moveAccept; + bool dropAccept; +}; + +class DragEventTester { +public: + DragEventTester() + : pos(60, 60) + , actions(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction) + , buttons(Qt::LeftButton) + , modifiers(Qt::NoModifier) + { + } + + ~DragEventTester() { + qDeleteAll(events); + events.clear(); + enterEvent = 0; + moveEvent = 0; + dropEvent = 0; + leaveEvent = 0; + } + + void addEnterEvent() + { + enterEvent = new QDragEnterEvent(pos, actions, &data, buttons, modifiers); + events.append(enterEvent); + } + + void addMoveEvent() + { + moveEvent = new QDragMoveEvent(pos, actions, &data, buttons, modifiers, QEvent::DragMove); + events.append(moveEvent); + } + + void addDropEvent() + { + dropEvent = new QDropEvent(pos, actions, &data, buttons, modifiers, QEvent::Drop); + events.append(dropEvent); + } + + void addLeaveEvent() + { + leaveEvent = new QDragLeaveEvent(); + events.append(leaveEvent); + } + + void sendDragEventSequence(QQuickWindow *window) const { + for (int i = 0; i < events.size(); ++i) { + QCoreApplication::sendEvent(window, events[i]); + } + } + + // Used for building events. + QMimeData data; + QPoint pos; + Qt::DropActions actions; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + + // Owns events. + QList events; + + // Non-owner pointers for easy acccess. + QDragEnterEvent *enterEvent; + QDragMoveEvent *moveEvent; + QDropEvent *dropEvent; + QDragLeaveEvent *leaveEvent; +}; + +void tst_qquickwindow::testDragEventPropertyPropagation() +{ + QQuickWindow window; + TestDropTarget dropTarget(window.contentItem()); + + // Setting the size is important because the QQuickWindow checks if the drag happened inside + // the drop target. + dropTarget.setSize(QSizeF(100, 100)); + + // Test enter events property propagation. + // For enter events, only isAccepted gets propagated. + { + DragEventTester builder; + dropTarget.enterAccept = false; + dropTarget.enterDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = false; + dropTarget.enterDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = true; + dropTarget.enterDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = true; + dropTarget.enterDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + + // Test move events property propagation. + // For move events, both isAccepted and dropAction get propagated. + dropTarget.reset(); + { + DragEventTester builder; + dropTarget.moveAccept = false; + dropTarget.moveDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = false; + dropTarget.moveDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = true; + dropTarget.moveDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = true; + dropTarget.moveDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + + // Test drop events property propagation. + // For drop events, both isAccepted and dropAction get propagated. + dropTarget.reset(); + { + DragEventTester builder; + dropTarget.dropAccept = false; + dropTarget.dropDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = false; + dropTarget.dropDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = true; + dropTarget.dropDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = true; + dropTarget.dropDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" From ec4c2e640dbcb35769ae3fb0699e42f07b3009db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 7 Mar 2017 14:51:20 +0100 Subject: [PATCH 20/26] Fix cache file generation for prefix and shadow builds For prefix builds generate the cache files in $$MODULE_BASE_OUTDIR/qml/ and install them from there. The use of relative paths for the qml cache extra compiler output is required because target_predeps in the extra compiler configuration will generate relative paths as dependency for the target. Task-number: QTBUG-58570 Change-Id: I6eedfd2aca1b0bdc7a230ce7521e499c6ab70ee0 Reviewed-by: Lars Knoll --- tools/qmlcachegen/qmlcache.prf | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tools/qmlcachegen/qmlcache.prf b/tools/qmlcachegen/qmlcache.prf index 5cac5592c3..31c18a231b 100644 --- a/tools/qmlcachegen/qmlcache.prf +++ b/tools/qmlcachegen/qmlcache.prf @@ -17,22 +17,36 @@ QML_CACHEGEN_ARGS=--target-architecture=$$QML_CACHEGEN_ARCH return() } +load(qt_build_paths) + +prefix_build: QMLCACHE_DESTDIR = $$MODULE_BASE_OUTDIR/qml/$$TARGETPATH +else: QMLCACHE_DESTDIR = $$[QT_INSTALL_QML]/$$TARGETPATH + CACHEGEN_FILES= +qmlcacheinst.files = for(qmlf, QML_FILES) { contains(qmlf,.*\\.js$)|contains(qmlf,.*\\.qml$) { - CACHEGEN_FILES += $$qmlf + CACHEGEN_FILES += $$absolute_path($$qmlf, $$_PRO_FILE_PWD_) + qmlcacheinst.files += $$QMLCACHE_DESTDIR/$$relative_path($$qmlf, $$_PRO_FILE_PWD_)c } } -qmlcachegen.input = CACHEGEN_FILES -prefix_build { - qmlcachegen.output = ${QMAKE_FILE_IN}c -} else { - qmlcachegen.output = $$[QT_INSTALL_QML]/$$TARGETPATH/${QMAKE_FILE_IN}c - qmlcachegen.CONFIG = no_link target_predeps +defineReplace(qmlCacheOutputFileName) { + return($$relative_path($$QMLCACHE_DESTDIR/$$relative_path($$1, $$_PRO_FILE_PWD_)c, $$OUT_PWD)) } + +qmlcacheinst.base = $$QMLCACHE_DESTDIR +qmlcacheinst.path = $$[QT_INSTALL_QML]/$$TARGETPATH +qmlcacheinst.CONFIG = no_check_exist + +qmlcachegen.input = CACHEGEN_FILES +qmlcachegen.output = ${QMAKE_FUNC_FILE_IN_qmlCacheOutputFileName} +qmlcachegen.CONFIG = no_link target_predeps qmlcachegen.commands = $$QML_CACHEGEN $$QML_CACHEGEN_ARGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN} qmlcachegen.variable_out = GENERATED_FILES -QMAKE_EXTRA_COMPILERS += qmlcachegen +!debug_and_release|!build_all|CONFIG(release, debug|release) { + QMAKE_EXTRA_COMPILERS += qmlcachegen + INSTALLS += qmlcacheinst +} From 38ea4e74ffcf1f5a82eee0146a5bb76e0750b63c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Mar 2017 15:56:26 +0100 Subject: [PATCH 21/26] Simplify build system integration Make the prf file available via COPIES for non-prefix builds and otherwise install it. Change-Id: Ie11b6653d250a4491c4f5897bc61a1efdd546d27 Reviewed-by: Lars Knoll --- tools/qmlcachegen/qmlcachegen.pro | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro index 81783d0396..25afc2860d 100644 --- a/tools/qmlcachegen/qmlcachegen.pro +++ b/tools/qmlcachegen/qmlcachegen.pro @@ -6,19 +6,9 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII SOURCES = qmlcachegen.cpp TARGET = qmlcachegen -BUILD_INTEGRATION = qmlcache.prf -!force_independent { - qmake_integration.input = BUILD_INTEGRATION - qmake_integration.output = $$[QT_HOST_DATA]/mkspecs/features/${QMAKE_FILE_BASE}.prf - qmake_integration.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} - qmake_integration.name = COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} - qmake_integration.CONFIG = no_clean no_link - !contains(TEMPLATE, vc.*): qmake_integration.variable_out = GENERATED_FILES - QMAKE_EXTRA_COMPILERS += qmake_integration -} - -qmake_integration_installs.files = $$BUILD_INTEGRATION -qmake_integration_installs.path = $$[QT_HOST_DATA]/mkspecs/features -INSTALLS += qmake_integration_installs +build_integration.files = qmlcache.prf +build_integration.path = $$[QT_HOST_DATA]/mkspecs/features +prefix_build: INSTALLS += build_integration +else: COPIES += build_integration load(qt_tool) From 55e558144d6d78062dd5d04f786e7fccf4eeebb7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Mar 2017 16:08:03 +0100 Subject: [PATCH 22/26] Fix time stamp related errors when loading AOT caches Cache files created ahead of time do not require a timestamp match towards the source file. This is because we cannot guarantee that all transport mechanism from source to deployment preserve the timestamp at the required resolution (if at all) and the source may also not be present at all (obfuscated deployment chosen). For cache files created at run-time however we'll continue to require time stamp verification. Change-Id: Ia7cdf3d063edd5bb1e6985089f1a666c970a0bd0 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compilationunitmapper.cpp | 2 +- src/qml/compiler/qv4compileddata.cpp | 4 ++-- tools/qmlcachegen/qmlcachegen.cpp | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index 2e1213464c..1ae0fb190c 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -76,7 +76,7 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const return false; } - { + if (header->sourceTimeStamp) { QFileInfo sourceCode(sourcePath); QDateTime sourceTimeStamp; if (sourceCode.exists()) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 70a03c2c1f..7854a49af9 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -361,7 +361,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; QScopedValueRollback dataPtrChange(data, mappedUnit); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { *errorString = QStringLiteral("QML source file has moved to a different location."); return false; } @@ -409,12 +409,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) { errorString->clear(); +#if !defined(V4_BOOTSTRAP) if (data->sourceTimeStamp == 0) { *errorString = QStringLiteral("Missing time stamp for source file"); return false; } -#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index fa75d14c8e..78159c38d1 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -96,7 +96,6 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString(); return false; } - irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); } { @@ -177,7 +176,6 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString(); return false; } - irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); } QQmlJS::Engine *engine = &irDocument.jsParserEngine; From e0add0b655941dcec483674594b63ce9775598be Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Mar 2017 17:09:55 +0100 Subject: [PATCH 23/26] Fix relocation related errors when loading AOT caches The original directory of the source file the cache was created from - when generating ahead of time - is unlikely going to be identical to the final location for example on a deployed device. Therefore when generating caches ahead of time, don't store the source path, don't attempt to verify it when loading and don't try to save the cache file at run-time again. We still need set the sourceFileIndex at load-time though, in order to make relative path url resolution work (for example source: "my.png" in an Image element). Change-Id: I3d6952f5d0a165cfa2cb400191a9f6ffe6be69f4 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 15 ++++++++++++++- src/qml/compiler/qv4jsir.cpp | 6 +----- src/qml/qml/qqmltypeloader.cpp | 5 ++++- tools/qmlcachegen/qmlcachegen.cpp | 4 ++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 7854a49af9..668f20e4f2 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -480,10 +480,22 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); QQmlRefPointer compilationUnit = irDocument->javaScriptCompilationUnit; - QV4::CompiledData::Unit *jsUnit = const_cast(irDocument->javaScriptCompilationUnit->data); + QV4::CompiledData::Unit *jsUnit = const_cast(compilationUnit->data); + auto ensureWritableUnit = [&jsUnit, &compilationUnit]() { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast(unitCopy); + } + }; QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; + if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) { + ensureWritableUnit(); + jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName); + } + // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize // the string table. @@ -546,6 +558,7 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) } if (!signalParameterNameTable.isEmpty()) { + ensureWritableUnit(); Q_ASSERT(jsUnit != compilationUnit->data); const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); uint newSize = jsUnit->unitSize + signalParameterTableSize; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 5687834b00..cc2f9b7cf2 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -348,11 +348,7 @@ Module::~Module() void Module::setFileName(const QString &name) { - if (fileName.isEmpty()) - fileName = name; - else { - Q_ASSERT(fileName == name); - } + fileName = name; } Function::Function(Module *module, Function *outer, const QString &name) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index f4f04e12c0..68eb989c70 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2407,6 +2407,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer m_document.reset(new QmlIR::Document(isDebugging())); QmlIR::IRLoader loader(unit->data, m_document.data()); loader.load(); + m_document->jsModule.setFileName(finalUrlString()); m_document->javaScriptCompilationUnit = unit; continueLoadFromIR(); } @@ -2507,6 +2508,8 @@ void QQmlTypeData::compile(const QQmlRefPointer &typeNameCach { Q_ASSERT(m_compiledData.isNull()); + const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation; + QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache); m_compiledData = compiler.compile(); @@ -2515,7 +2518,7 @@ void QQmlTypeData::compile(const QQmlRefPointer &typeNameCach return; } - const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode; + const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; if (trySaveToDisk) { QString errorString; if (m_compiledData->saveToDisk(url(), &errorString)) { diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 78159c38d1..eb2986d957 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -112,7 +112,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi } { - QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); for (QmlIR::Object *object: qAsConst(irDocument.objects)) { if (object->functionsAndExpressions->count == 0) continue; @@ -215,7 +215,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil { QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); - v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode); + v4CodeGen.generateFromProgram(/*empty input file name*/QString(), sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode); QList jsErrors = v4CodeGen.errors(); if (!jsErrors.isEmpty()) { for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) { From 42e098fa0ad318a6b6fafadbadce21b974c29c4d Mon Sep 17 00:00:00 2001 From: Oleg Yadrov Date: Mon, 27 Feb 2017 13:15:52 -0800 Subject: [PATCH 24/26] QtQuick scene graph: fix text native rendering Only 65536 vertices (65536 / 4 = 16384 characters) can be drawn in one draw call. This is why QSGDistanceFieldGlyphNode (renderType: Text.QtRendering) creates subnodes if number of characters exceeds that limit. QSGDefaultGlyphNode (renderType: Text.NativeRendering) missed that logic for some reason. Task-number: QTBUG-58852 Change-Id: I88b3fcdb8e56bc92622d3347cd638634d43df138 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/scenegraph/qsgdefaultglyphnode.cpp | 128 +++++++++++++++++++ src/quick/scenegraph/qsgdefaultglyphnode_p.h | 23 ++++ 2 files changed, 151 insertions(+) diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index b856d99bc1..0d42102f36 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -42,11 +42,33 @@ QT_BEGIN_NAMESPACE +QSGDefaultGlyphNode::QSGDefaultGlyphNode() + : m_glyphNodeType(RootGlyphNode) + , m_dirtyGeometry(false) +{ + setFlag(UsePreprocess); +} + +QSGDefaultGlyphNode::~QSGDefaultGlyphNode() +{ + if (m_glyphNodeType == SubGlyphNode) + return; + + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); +} + void QSGDefaultGlyphNode::setMaterialColor(const QColor &color) { static_cast(m_material)->setColor(color); } +void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + QSGBasicGlyphNode::setGlyphs(position, glyphs); + m_dirtyGeometry = true; +} + void QSGDefaultGlyphNode::update() { QRawFont font = m_glyphs.rawFont(); @@ -84,4 +106,110 @@ void QSGDefaultGlyphNode::update() markDirty(DirtyGeometry); } +void QSGDefaultGlyphNode::preprocess() +{ + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); + + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGDefaultGlyphNode::updateGeometry() +{ + // Remove previously created sub glyph nodes + // We assume all the children are sub glyph nodes + QSGNode *subnode = firstChild(); + while (subnode) { + // We can't delete the node now as it might be in the preprocess list + // It will be deleted in the next preprocess + m_nodesToDelete.append(subnode); + subnode = subnode->nextSibling(); + } + removeAllChildNodes(); + + GlyphInfo glyphInfo; + + const QVector indexes = m_glyphs.glyphIndexes(); + const QVector positions = m_glyphs.positions(); + + const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384 + const int maxVertices = maxGlyphs * 4; // 65536 + const int maxIndexes = maxGlyphs * 6; // 98304 + + for (int i = 0; i < indexes.size(); ++i) { + const int glyphIndex = indexes.at(i); + const QPointF position = positions.at(i); + + // As we use UNSIGNED_SHORT indexing in the geometry, we overload the + // "glyphsInOtherNodes" concept as overflow for if there are more than + // 65536 (16384 * 4) vertices to render which would otherwise exceed + // the maximum index size. This will cause sub-nodes to be recursively + // created to handle any number of glyphs. + if (i >= maxGlyphs) { + glyphInfo.indexes.append(glyphIndex); + glyphInfo.positions.append(position); + continue; + } + } + + if (!glyphInfo.indexes.isEmpty()) { + QGlyphRun subNodeGlyphRun(m_glyphs); + subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes); + subNodeGlyphRun.setPositions(glyphInfo.positions); + + QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(); + subNode->setGlyphNodeType(SubGlyphNode); + subNode->setColor(m_color); + subNode->setStyle(m_style); + subNode->setStyleColor(m_styleColor); + subNode->setGlyphs(m_position, subNodeGlyphRun); + subNode->update(); + subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered + appendChildNode(subNode); + + QSGGeometry *g = geometry(); + + QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D(); + quint16 *indexData = g->indexDataAsUShort(); + + QVector tempVertexData(maxVertices); + QVector tempIndexData(maxIndexes); + + for (int i = 0; i < maxGlyphs; i++) { + tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0]; + tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1]; + tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2]; + tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3]; + + tempIndexData[i * 6 + 0] = indexData[i * 6 + 0]; + tempIndexData[i * 6 + 1] = indexData[i * 6 + 1]; + tempIndexData[i * 6 + 2] = indexData[i * 6 + 2]; + tempIndexData[i * 6 + 3] = indexData[i * 6 + 3]; + tempIndexData[i * 6 + 4] = indexData[i * 6 + 4]; + tempIndexData[i * 6 + 5] = indexData[i * 6 + 5]; + } + + g->allocate(maxVertices, maxIndexes); + vertexData = g->vertexDataAsTexturedPoint2D(); + indexData = g->indexDataAsUShort(); + + for (int i = 0; i < maxGlyphs; i++) { + vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0]; + vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1]; + vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2]; + vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3]; + + indexData[i * 6 + 0] = tempIndexData[i * 6 + 0]; + indexData[i * 6 + 1] = tempIndexData[i * 6 + 1]; + indexData[i * 6 + 2] = tempIndexData[i * 6 + 2]; + indexData[i * 6 + 3] = tempIndexData[i * 6 + 3]; + indexData[i * 6 + 4] = tempIndexData[i * 6 + 4]; + indexData[i * 6 + 5] = tempIndexData[i * 6 + 5]; + } + } + + m_dirtyGeometry = false; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index 0eb7a4e4bd..37a89c70b9 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -59,8 +59,31 @@ QT_BEGIN_NAMESPACE class QSGDefaultGlyphNode : public QSGBasicGlyphNode { public: + QSGDefaultGlyphNode(); + ~QSGDefaultGlyphNode(); void setMaterialColor(const QColor &color) override; + void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override; void update() override; + void preprocess() override; + void updateGeometry(); + +private: + enum DefaultGlyphNodeType { + RootGlyphNode, + SubGlyphNode + }; + + void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; } + + DefaultGlyphNodeType m_glyphNodeType; + QLinkedList m_nodesToDelete; + + struct GlyphInfo { + QVector indexes; + QVector positions; + }; + + uint m_dirtyGeometry: 1; }; QT_END_NAMESPACE From 182469fc85257fccfa8024e132ad40a50b03c0f5 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Thu, 9 Mar 2017 13:59:40 +0100 Subject: [PATCH 25/26] Build fix for -no-feature-quick-shadereffect Change-Id: Ie1f601c6ae4c6c5d8d23b14a6670979d9c24e209 Reviewed-by: Simon Hausmann --- src/imports/imports.pro | 2 +- src/quick/items/context2d/qquickcanvasitem.cpp | 1 + src/quick/items/context2d/qquickcontext2d.cpp | 2 ++ src/quick/items/qquickpainteditem_p.h | 1 + src/quick/util/qquickanimatorjob.cpp | 3 ++- src/src.pro | 2 +- 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 45719df874..0ba949f070 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -20,7 +20,7 @@ qtHaveModule(quick) { sharedimage \ testlib - qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ + qtConfig(quick-shadereffect):qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ SUBDIRS += particles } diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 1167f408f5..a81ab619ac 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index e25cc5ccbe..0eeba5d6cd 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -42,7 +42,9 @@ #include "qquickcanvasitem_p.h" #include #include +#if QT_CONFIG(quick_shadereffect) #include +#endif #include #include diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h index 742e786335..229fbf1007 100644 --- a/src/quick/items/qquickpainteditem_p.h +++ b/src/quick/items/qquickpainteditem_p.h @@ -52,6 +52,7 @@ // #include "qquickitem_p.h" +#include "qquickpainteditem.h" #include QT_BEGIN_NAMESPACE diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 4aacb09c97..89007cff1f 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -345,12 +345,13 @@ void QQuickTransformAnimatorJob::postSync() } QQuickItemPrivate *d = QQuickItemPrivate::get(m_target); +#if QT_CONFIG(quick_shadereffect) if (d->extra.isAllocated() && d->extra->layer && d->extra->layer->enabled()) { d = QQuickItemPrivate::get(d->extra->layer->m_effectSource); } - +#endif m_helper->node = d->itemNode(); } diff --git a/src/src.pro b/src/src.pro index 4e2de8da14..24e139415b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -10,7 +10,7 @@ qtHaveModule(gui):qtConfig(animation) { quick \ qmltest - qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ + qtConfig(quick-shadereffect):qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ SUBDIRS += particles qtHaveModule(widgets): SUBDIRS += quickwidgets } From 77e0dc0485953427320ed0b442ba24eef4f9d73b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 10 Mar 2017 09:17:35 +0100 Subject: [PATCH 26/26] Fix support for enums and translations in list elements QQC uses valid constructs like ListElement { dayOfWeek: Locale.Sunday }, where the enums are resolved at type compilation time. Syntactically Locale.Sunday is a JS expression, but as scripts are not permitted for list elements, we must retain the values in string form when generating the binary (cache) qml representation. This is a forward port of commit 736f4f9c847d1102f6ac77674c831f0555ff445e from the qml compiler, and also matches QTRD-3226. Change-Id: I3e615f224d4ab222a50f3271735cb8f7a24f5f11 Reviewed-by: Erik Verbruggen --- tools/qmlcachegen/qmlcachegen.cpp | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index eb2986d957..9a68e2ac97 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -80,6 +80,39 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic return message; } +// Ensure that ListElement objects keep all property assignments in their string form +static void annotateListElements(QmlIR::Document *document) +{ + QStringList listElementNames; + + foreach (const QV4::CompiledData::Import *import, document->imports) { + const QString uri = document->stringAt(import->uriIndex); + if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick")) + continue; + + QString listElementName = QStringLiteral("ListElement"); + const QString qualifier = document->stringAt(import->qualifierIndex); + if (!qualifier.isEmpty()) { + listElementName.prepend(QLatin1Char('.')); + listElementName.prepend(qualifier); + } + listElementNames.append(listElementName); + } + + if (listElementNames.isEmpty()) + return; + + foreach (QmlIR::Object *object, document->objects) { + if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex))) + continue; + for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex)); + } + } +} + static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); @@ -111,6 +144,8 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi } } + annotateListElements(&irDocument); + { QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); for (QmlIR::Object *object: qAsConst(irDocument.objects)) {