From fa3de804cb5e09c4908f21a7b428ab073d9c6c32 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 8 Sep 2021 17:08:19 +0200 Subject: MultiPointTouchArea: remap touchpoint positions when filtering When MPTA filters a touch event during delivery to a child item, QEventPoint::position() is relative to the child; but MPTA updates its stored QQuickTouchPoint instances from the event's touchpoints and emits pressed(). Now it will first remap them into its own coordinates. Fixes: QTBUG-74028 Change-Id: Ia4e64aa829d9d4d452a03bc964f06c729baa8f78 Reviewed-by: Fabian Kosmale (cherry picked from commit 664f986571696414fe2446f80c1b45edf9961999) Reviewed-by: Shawn Rutledge --- src/quick/items/qquickmultipointtoucharea.cpp | 26 +++++++++++++--------- src/quick/items/qquickmultipointtoucharea_p.h | 7 +++--- .../data/nestedMouseArea.qml | 26 ++++++++++++++++++++++ .../tst_qquickmultipointtoucharea.cpp | 18 +++++++++++++++ 4 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 480731aee9..9a44785c93 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -569,7 +569,7 @@ void QQuickMultiPointTouchArea::grabGesture() setKeepTouchGrab(true); } -void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) +void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints remap) { bool ended = false; bool moved = false; @@ -578,12 +578,14 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) clearTouchLists(); QList touchPoints; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); + bool touchPointsFromEvent = false; switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: touchPoints = static_cast(event)->touchPoints(); + touchPointsFromEvent = true; break; case QEvent::MouseButtonPress: _mouseQpaTouchPoint = QTouchEvent::TouchPoint(windowPriv->touchMouseId); @@ -614,6 +616,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) qWarning("updateTouchData: unhandled event type %d", event->type()); break; } + if (!touchPointsFromEvent) + remap = RemapEventPoints::No; int numTouchPoints = touchPoints.count(); //always remove released touches, and make sure we handle all releases before adds. @@ -624,7 +628,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); if (!dtp) continue; - updateTouchPoint(dtp, &p); + updateTouchPoint(dtp, &p, RemapEventPoints::No); dtp->setPressed(false); _releasedTouchPoints.append(dtp); _touchPoints.remove(id); @@ -639,19 +643,19 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) //handled above } else if (!_touchPoints.contains(id)) { //could be pressed, moved, or stationary // (we may have just obtained enough points to start tracking them -- in that case moved or stationary count as newly pressed) - addTouchPoint(&p); + addTouchPoint(&p, remap); started = true; } else if ((touchPointState & Qt::TouchPointMoved) || p.d->stationaryWithModifiedProperty) { // React to a stationary point with a property change (velocity, pressure) as if the point moved. (QTBUG-77142) QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); Q_ASSERT(dtp); _movedTouchPoints.append(dtp); - updateTouchPoint(dtp,&p); + updateTouchPoint(dtp, &p, remap); moved = true; } else { QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); Q_ASSERT(dtp); - updateTouchPoint(dtp,&p); + updateTouchPoint(dtp, &p, remap); } } @@ -707,7 +711,7 @@ void QQuickMultiPointTouchArea::clearTouchLists() _movedTouchPoints.clear(); } -void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p) +void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p, RemapEventPoints remap) { QQuickTouchPoint *dtp = nullptr; for (QQuickTouchPoint* tp : qAsConst(_touchPrototypes)) { @@ -721,7 +725,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p) if (dtp == nullptr) dtp = new QQuickTouchPoint(false); dtp->setPointId(p->id()); - updateTouchPoint(dtp,p); + updateTouchPoint(dtp, p, remap); dtp->setPressed(true); _touchPoints.insert(p->id(),dtp); _pressedTouchPoints.append(dtp); @@ -782,12 +786,12 @@ void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype) _touchPrototypes.insert(id, prototype); } -void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p) +void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p, RemapEventPoints remap) { //TODO: if !qmlDefined, could bypass setters. // also, should only emit signals after all values have been set dtp->setUniqueId(p->uniqueId()); - dtp->setPosition(p->pos()); + dtp->setPosition(remap == RemapEventPoints::ToLocal ? mapFromScene(p->scenePos()) : p->pos()); dtp->setEllipseDiameters(p->ellipseDiameters()); dtp->setPressure(p->pressure()); dtp->setRotation(p->rotation()); @@ -975,12 +979,12 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve } if (!shouldFilter(event)) return false; - updateTouchData(event); + updateTouchData(event, RemapEventPoints::ToLocal); return _stealMouse; case QEvent::TouchEnd: { if (!shouldFilter(event)) return false; - updateTouchData(event); + updateTouchData(event, RemapEventPoints::ToLocal); ungrab(true); } break; diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 8fc01bc4e1..fc3f2fe631 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -267,14 +267,15 @@ protected: void mouseUngrabEvent() override; void touchUngrabEvent() override; + enum class RemapEventPoints { No, ToLocal }; void addTouchPrototype(QQuickTouchPoint* prototype); - void addTouchPoint(const QTouchEvent::TouchPoint *p); + void addTouchPoint(const QTouchEvent::TouchPoint *p, RemapEventPoints remap); void addTouchPoint(const QMouseEvent *e); void clearTouchLists(); - void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*); + void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*, RemapEventPoints remap); void updateTouchPoint(QQuickTouchPoint *dtp, const QMouseEvent *e); - void updateTouchData(QEvent*); + void updateTouchData(QEvent*, RemapEventPoints remap = RemapEventPoints::No); bool sendMouseEvent(QMouseEvent *event); bool shouldFilter(QEvent *event); diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml new file mode 100644 index 0000000000..7ca3f187d6 --- /dev/null +++ b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml @@ -0,0 +1,26 @@ +import QtQuick 2.12 + +Item { + id: root + width: 300; height: 300 + property point mptaPoint + property point maPoint + MultiPointTouchArea { + anchors.fill : parent + onPressed: function(touchPoints) { + root.mptaPoint = Qt.point(touchPoints[0].x, touchPoints[0].y) + } + MouseArea { + id: ma + width: 100; height: 100 + anchors.centerIn: parent + onPressed: function(mouse) { + root.maPoint = Qt.point(mouse.x, mouse.y) + } + } + Rectangle { + anchors.fill: ma + border.color: "grey" + } + } +} diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index a9d557915a..3aff9a293a 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -78,6 +78,7 @@ private slots: void cancel(); void stationaryTouchWithChangingPressure(); void nestedPinchAreaMouse(); + void touchFiltering(); private: QQuickView *createAndShowView(const QString &file); @@ -1456,6 +1457,23 @@ void tst_QQuickMultiPointTouchArea::nestedPinchAreaMouse() QCOMPARE(mpta->property("releasedCount").toInt(), 1); } +void tst_QQuickMultiPointTouchArea::touchFiltering() // QTBUG-74028 +{ + QScopedPointer window(createAndShowView("nestedMouseArea.qml")); + QVERIFY(window->rootObject() != nullptr); + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild(); + QVERIFY(mpta); + QQuickMouseArea *ma = window->rootObject()->findChild(); + QVERIFY(ma); + + QSignalSpy mptaSpy(mpta, &QQuickMultiPointTouchArea::pressed); + const QPoint pt = window->rootObject()->boundingRect().center().toPoint(); + QTest::touchEvent(window.data(), device).press(1, pt); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(mpta->parentItem()->property("mptaPoint").toPoint(), pt); + QCOMPARE(mpta->parentItem()->property("maPoint").toPoint(), ma->boundingRect().center().toPoint()); + QCOMPARE(mptaSpy.count(), 1); +} QTEST_MAIN(tst_QQuickMultiPointTouchArea) -- cgit v1.2.3 From 1fe90c8d2e17de9676c3cc7ba55c81422bebc0e7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 18 May 2022 13:21:05 +0200 Subject: MouseArea: don't override preventStealing on mouse release In setPreventStealing() we call setKeepMouseGrab(); so it does not make sense to override setKeepMouseGrab() in other places without even checking the state of the preventStealing property. Fixes: QTBUG-103522 Change-Id: Ib4a2b01b814835715642aec83fac0a84debe2461 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Fabian Kosmale (cherry picked from commit d92f1dfe05370c4a2ca7d86810e00b7466f454ae) Reviewed-by: Shawn Rutledge --- src/quick/items/qquickmousearea.cpp | 3 +- .../data/preventStealingListViewChild.qml | 31 ++++++++++++++++++++ .../quick/qquickmousearea/tst_qquickmousearea.cpp | 34 ++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 7f5272a142..405f7848e9 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -806,7 +806,8 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) QQuickWindow *w = window(); if (w && w->mouseGrabberItem() == this) ungrabMouse(); - setKeepMouseGrab(false); + if (!d->preventStealing) + setKeepMouseGrab(false); } } d->doubleClick = false; diff --git a/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml new file mode 100644 index 0000000000..3833c5a8a9 --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml @@ -0,0 +1,31 @@ +import QtQuick 2.15 + +ListView { + id: flick + width: 640 + height: 480 + model: 100 + + delegate: Rectangle { + border.color: "#81e889" + width: 640; height: 100 + Text { text: "Row " + index } + } + + Rectangle { + anchors.right: parent.right + anchors.margins: 2 + color: ma.pressed ? "#81e889" : "#c2f4c6" + width: 50; height: 50 + radius: 5 + MouseArea { + id: ma + anchors.fill: parent + preventStealing: true + drag { + target: parent + axis: Drag.YAxis + } + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 27fee2aab3..941d6dc47b 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -129,6 +129,7 @@ private slots: void invalidClick(); void pressedOrdering(); void preventStealing(); + void preventStealingListViewChild(); void clickThrough(); void hoverPosition(); void hoverPropagation(); @@ -1212,6 +1213,39 @@ void tst_QQuickMouseArea::preventStealing() QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p); } +// QTBUG-103522 +void tst_QQuickMouseArea::preventStealingListViewChild() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("preventStealingListViewChild.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QQuickFlickable *flickable = qobject_cast(window.rootObject()); + QVERIFY(flickable); + QQuickMouseArea *mouseArea = flickable->findChild(); + QVERIFY(mouseArea); + QPoint p = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint(); + const int threshold = qApp->styleHints()->startDragDistance(); + + flickable->flick(0, -10000); + for (int i = 0; i < 2; ++i) { + QVERIFY(flickable->isMovingVertically()); + QTest::touchEvent(&window, device).press(0, p); + QQuickTouchUtils::flush(&window); + for (int j = 0; j < 4 && !mouseArea->drag()->active(); ++j) { + p += QPoint(0, threshold); + QTest::touchEvent(&window, device).move(0, p); + QQuickTouchUtils::flush(&window); + } + // MouseArea should be dragged because of preventStealing; ListView does not steal the grab. + QVERIFY(mouseArea->drag()->active()); + QCOMPARE(flickable->isDragging(), false); + QTest::touchEvent(&window, device).release(0, p); + QCOMPARE(mouseArea->drag()->active(), false); + } +} + void tst_QQuickMouseArea::clickThrough() { //With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers -- cgit v1.2.3 From 56bf9a42525ec839053c2b8ae5ade03e0b59974d Mon Sep 17 00:00:00 2001 From: Ivan Tkachenko Date: Tue, 31 May 2022 23:32:06 +0300 Subject: Doc: QQuickTextInput: Mark all readonly properties as such Change-Id: I5fa12286ac594bafff89a56358bdda4051733e05 Reviewed-by: Shawn Rutledge (cherry picked from commit cb2cccd5578ffcfcbf6793c0a445022a6fd4a8ae) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextinput.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 8ceb8827f5..7814fd94b6 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -518,8 +518,16 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color) } /*! - \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment \qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment + \readonly + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextInput, use the read-only property \c effectiveHorizontalAlignment. +*/ +/*! + \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment \qmlproperty enumeration QtQuick::TextInput::verticalAlignment Sets the horizontal alignment of the text within the TextInput item's @@ -542,7 +550,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color) When using the attached property LayoutMirroring::enabled to mirror application layouts, the horizontal alignment of text will also be mirrored. However, the property \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment - of TextInput, use the read-only property \c effectiveHorizontalAlignment. + of TextInput, use the read-only property \l effectiveHorizontalAlignment. */ QQuickTextInput::HAlignment QQuickTextInput::hAlign() const { @@ -862,6 +870,7 @@ void QQuickTextInput::setCursorPosition(int cp) /*! \qmlproperty rectangle QtQuick::TextInput::cursorRectangle + \readonly The rectangle where the standard text cursor is rendered within the text input. Read only. @@ -903,6 +912,7 @@ QRectF QQuickTextInput::cursorRectangle() const This property is read-only. To change the selection, use select(start,end), selectAll(), or selectWord(). + \readonly \sa selectionEnd, cursorPosition, selectedText */ int QQuickTextInput::selectionStart() const @@ -918,6 +928,7 @@ int QQuickTextInput::selectionStart() const This property is read-only. To change the selection, use select(start,end), selectAll(), or selectWord(). + \readonly \sa selectionStart, cursorPosition, selectedText */ int QQuickTextInput::selectionEnd() const @@ -948,6 +959,7 @@ void QQuickTextInput::select(int start, int end) /*! \qmlproperty string QtQuick::TextInput::selectedText + \readonly This read-only property provides the text currently selected in the text input. @@ -2473,6 +2485,7 @@ void QQuickTextInput::setPersistentSelection(bool on) /*! \qmlproperty bool QtQuick::TextInput::canPaste + \readonly Returns true if the TextInput is writable and the content of the clipboard is suitable for pasting into the TextInput. @@ -2494,6 +2507,7 @@ bool QQuickTextInput::canPaste() const /*! \qmlproperty bool QtQuick::TextInput::canUndo + \readonly Returns true if the TextInput is writable and there are previous operations that can be undone. @@ -2507,6 +2521,7 @@ bool QQuickTextInput::canUndo() const /*! \qmlproperty bool QtQuick::TextInput::canRedo + \readonly Returns true if the TextInput is writable and there are \l {undo}{undone} operations that can be redone. @@ -2520,6 +2535,7 @@ bool QQuickTextInput::canRedo() const /*! \qmlproperty real QtQuick::TextInput::contentWidth + \readonly Returns the width of the text, including the width past the width which is covered due to insufficient wrapping if \l wrapMode is set. @@ -2533,6 +2549,7 @@ qreal QQuickTextInput::contentWidth() const /*! \qmlproperty real QtQuick::TextInput::contentHeight + \readonly Returns the height of the text, including the height past the height that is covered if the text does not fit within the set height. @@ -2696,7 +2713,7 @@ void QQuickTextInput::focusOutEvent(QFocusEvent *event) /*! \qmlproperty bool QtQuick::TextInput::inputMethodComposing - + \readonly This property holds whether the TextInput has partial text input from an input method. -- cgit v1.2.3 From 669124284cef25eb6c3ee6dfa0c5298511a3cf21 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 1 Jun 2022 13:18:32 +0200 Subject: QQmlListCompositor: suppress GCC 12 -Warray-bounds warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Despite my best efforts in teaching GCC 12 to understand that groupCount is always <= MaxGroupCount, it continued to issue these warnings: In member function ‘void QQmlListCompositor::iterator::incrementIndexes(int, uint)’, inlined from ‘void QQmlListCompositor::iterator::incrementIndexes(int)’ at qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:138:65, inlined from ‘void QQmlListCompositor::move(Group, int, Group, int, int, Group, QVector*, QVector*)’ at qtdeclarative/src/qmlmodels/qqmllistcompositor.cpp:852:40: qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:336:20: error: array subscript 28 is above array bounds of ‘int [11]’ [-Werror=array-bounds] 336 | index[i] += difference; | ~~~~~~~^ qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h: In member function ‘void QQmlListCompositor::move(Group, int, Group, int, int, Group, QVector*, QVector*)’: qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:154:13: note: while referencing ‘QQmlListCompositor::iterator::index’ 154 | int index[MaximumGroupCount] = { 0 }; | ^~~~~ In member function ‘void QQmlListCompositor::iterator::incrementIndexes(int, uint)’, inlined from ‘void QQmlListCompositor::iterator::incrementIndexes(int)’ at qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:138:65, inlined from ‘void QQmlListCompositor::move(Group, int, Group, int, int, Group, QVector*, QVector*)’ at qtdeclarative/src/qmlmodels/qqmllistcompositor.cpp:852:40: qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:336:20: error: array subscript 28 is above array bounds of ‘int [11]’ [-Werror=array-bounds] 336 | index[i] += difference; | ~~~~~~~^ qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h: In member function ‘void QQmlListCompositor::move(Group, int, Group, int, int, Group, QVector*, QVector*)’: qtdeclarative/src/qmlmodels/qqmllistcompositor_p.h:154:13: note: while referencing ‘QQmlListCompositor::iterator::index’ 154 | int index[MaximumGroupCount] = { 0 }; | ^~~~~ cc1plus: all warnings being treated as errors Therefore, I see no other solution than to suppress the warning the hard way. Fixes: QTBUG-103924 Change-Id: Ic46009daa35357c6e6bb0fd1c16d020e360b7525 Reviewed-by: Thiago Macieira (cherry picked from commit 80f0bf64e49f07a73712998ddc8a0eebd1b660b6) Reviewed-by: Ulf Hermann --- src/qmlmodels/qqmllistcompositor_p.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qmlmodels/qqmllistcompositor_p.h b/src/qmlmodels/qqmllistcompositor_p.h index 80de0be349..27e8a5b429 100644 --- a/src/qmlmodels/qqmllistcompositor_p.h +++ b/src/qmlmodels/qqmllistcompositor_p.h @@ -311,6 +311,10 @@ Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE); inline QQmlListCompositor::iterator::iterator() {} +QT_WARNING_PUSH +// GCC isn't wrong, as groupCount is public in iterator, but we tried Q_ASSUME(), +// right in front of the loops, and it didn't help, so we disable the warning: +QT_WARNING_DISABLE_GCC("-Warray-bounds") inline QQmlListCompositor::iterator::iterator( Range *range, int offset, Group group, int groupCount) : range(range) @@ -338,6 +342,7 @@ inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint index[i] -= difference; } } +QT_WARNING_POP // -Warray-bounds inline QQmlListCompositor::insert_iterator::insert_iterator( Range *range, int offset, Group group, int groupCount) -- cgit v1.2.3 From b368ffc8c6c804895722b165222da72c77e23053 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 27 May 2022 09:45:38 +0200 Subject: QQmlDebug: reliably print the debugger warning When an executable contains some TUs that pass printWarning and some that don't, the warning could have been lost if static initialization first initialized one that had printWarning=false and only then moved to initializing one that had printWarning=true. Typically, a DLL might pass false here, in which case the user application, initialized later, wouldn't have a say in whether the warning was printed. Use an atomic_flag to independently track whether the warning was printed, so that we reliably print the warning when at least one TU in the final executable requested it. [ChangeLog][QtQml][QQmlDebug] The warning about enabled debuggers is now printed when at least one translation unit in the final executable requests it, independent of the order in which translation units are linked/initialized. Manual conflict resolutions: - dropped the Q_CONSTINIT (doesn't exist in 5.15) Change-Id: I10af0a46ecb82a8b1a1373eb9332d913c03b20f3 Reviewed-by: Ulf Hermann (cherry picked from commit c98c2c08a5f926b67c40b6363274d23066bfe341) --- src/qml/debugger/qqmldebug.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 05c8058840..6bf49b9671 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -44,15 +44,24 @@ #include #include +#include #include QT_REQUIRE_CONFIG(qml_debug); QT_BEGIN_NAMESPACE +#if __cplusplus >= 202002L +# define Q_ATOMIC_FLAG_INIT {} +#else +# define Q_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT // deprecated in C++20 +#endif + +static std::atomic_flag s_printedWarning = Q_ATOMIC_FLAG_INIT; + QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) { - if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning) + if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed)) fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n"); QQmlEnginePrivate::qml_debugging_enabled = true; } -- cgit v1.2.3 From 4cc5822d188e070501005a0bf508af1968cc826d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 23 May 2022 07:12:14 +0200 Subject: qqmlimport.cpp: remove unused qreadwritelock.h Amends 3fa8540800c43ccc4e4c1f8f323fb5ade34044db. Change-Id: I942c81107065ba5348c7e7d98904618c469b745c Reviewed-by: Fabian Kosmale (cherry picked from commit 6e2d70f2b8a0a02d01ca9dba759bd3d5eec32235) Reviewed-by: Qt CI Bot Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlimport.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 10c1b83285..89ba794b4b 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 15f637ebe729cfb10309fce67bc7f8d29f12a5c4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 27 May 2022 10:03:00 +0200 Subject: Fix race condition on QQmlEnginePrivate::qml_debugging_enabled C++11 allows static dynamic initialization from different TUs to happen concurrently, which means the QQmlDebuggingEnabler ctor must be re-entrant and synchronized with other users of qml_debugging_enabled. Thankfully, this is just a flag, so the fix is to simply make it atomic<> and use relaxed loads and stores on it. Manual conflict resolutions: - dropped the Q_CONSTINIT (doesn't exist in 5.15) - actual line conflicts in src/qml/qml/qqmlengine_p.h Change-Id: I0305ab55be86a0e286016a3d1d97ee9bc0e28070 Reviewed-by: Ulf Hermann (cherry picked from commit 0434e54b55d9a31c3b5331856bab639b7149058c) Reviewed-by: Fabian Kosmale --- src/qml/debugger/qqmldebug.cpp | 2 +- src/qml/debugger/qqmldebugconnector.cpp | 2 +- src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmlengine_p.h | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 6bf49b9671..4af711171a 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -63,7 +63,7 @@ QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) { if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed)) fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n"); - QQmlEnginePrivate::qml_debugging_enabled = true; + QQmlEnginePrivate::qml_debugging_enabled.store(true, std::memory_order_relaxed); } /*! diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp index 690d98c17f..72043ecd1f 100644 --- a/src/qml/debugger/qqmldebugconnector.cpp +++ b/src/qml/debugger/qqmldebugconnector.cpp @@ -111,7 +111,7 @@ QQmlDebugConnector *QQmlDebugConnector::instance() if (!params) return nullptr; - if (!QQmlEnginePrivate::qml_debugging_enabled) { + if (!QQmlEnginePrivate::qml_debugging_enabled.load(std::memory_order_relaxed)) { if (!params->arguments.isEmpty()) { qWarning().noquote() << QString::fromLatin1( "QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging " diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ba87c9810f..5c6a7b1f37 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -193,7 +193,7 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, \endcode */ -bool QQmlEnginePrivate::qml_debugging_enabled = false; +std::atomic QQmlEnginePrivate::qml_debugging_enabled{false}; bool QQmlEnginePrivate::s_designerMode = false; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 5e78471a5b..147024044c 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -80,6 +80,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QQmlContext; @@ -267,7 +269,7 @@ public: static bool designerMode(); static void activateDesignerMode(); - static bool qml_debugging_enabled; + static std::atomic qml_debugging_enabled; mutable QMutex networkAccessManagerMutex; -- cgit v1.2.3 From 08287b77cfbd1f30bf5190bf2a00273f5bf47569 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 14 Jun 2022 11:03:47 +0200 Subject: Stop using the same buffer for vertex and index data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) This does not work with Intel's OpenGL implementation from the last few years (at least since around 2018). One simply gets artifacts, probably related to updating the data in the buffer (as initial drawing seems correct, but subsequent frames, if there is a change in geometry, tend to get rendering errors sooner or later). 2) Due to the webgl platform plugin this has already been optional, using a runtime check. Now we just remove the conditional and always behave as is separateIndexBuffer was true. 3) This matches Qt 6. An index buffer is always separate in Qt 6. 4) GL_ARB_vertex_buffer_object says: "Note that it is expected that implementations may have different memory type requirements for efficient storage of indices and vertices. For example, some systems may prefer indices in AGP memory and vertices in video memory, or vice versa; or, on systems where DMA of index data is not supported, index data must be stored in (cacheable) system memory for acceptable performance. As a result, applications are strongly urged to put their models' vertex and index data in separate buffers, to assist drivers in choosing the most efficient locations." Fixes: QTBUG-69538 Change-Id: I0c56f401a202a2ba2c1f8aff4b1c4f9ff6d6ae31 Reviewed-by: Christian Strømme --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 68 ++++++---------------- .../scenegraph/coreapi/qsgopenglvisualizer.cpp | 2 +- src/quick/scenegraph/qsgdefaultrendercontext.cpp | 15 ----- src/quick/scenegraph/qsgdefaultrendercontext_p.h | 1 - 4 files changed, 18 insertions(+), 68 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index f8f3639f36..62631aa98b 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1068,11 +1068,10 @@ static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs) free(buffer->data); } -static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIndexBuffer) +static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs) { qsg_wipeBuffer(&batch->vbo, funcs); - if (separateIndexBuffer) - qsg_wipeBuffer(&batch->ibo, funcs); + qsg_wipeBuffer(&batch->ibo, funcs); delete batch->ubuf; batch->stencilClipState.reset(); delete batch; @@ -1082,13 +1081,12 @@ Renderer::~Renderer() { if (m_rhi || QOpenGLContext::currentContext()) { // Clean up batches and buffers - const bool separateIndexBuffer = m_context->separateIndexBuffer(); for (int i = 0; i < m_opaqueBatches.size(); ++i) - qsg_wipeBatch(m_opaqueBatches.at(i), this, separateIndexBuffer); + qsg_wipeBatch(m_opaqueBatches.at(i), this); for (int i = 0; i < m_alphaBatches.size(); ++i) - qsg_wipeBatch(m_alphaBatches.at(i), this, separateIndexBuffer); + qsg_wipeBatch(m_alphaBatches.at(i), this); for (int i = 0; i < m_batchPool.size(); ++i) - qsg_wipeBatch(m_batchPool.at(i), this, separateIndexBuffer); + qsg_wipeBatch(m_batchPool.at(i), this); } for (Node *n : qAsConst(m_nodes)) @@ -1162,8 +1160,7 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf) if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) { // Common case, use a shared memory pool for uploading vertex data to avoid // excessive reevaluation - QDataBuffer &pool = m_context->separateIndexBuffer() && isIndexBuf - ? m_indexUploadPool : m_vertexUploadPool; + QDataBuffer &pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool; if (byteSize > pool.size()) pool.resize(byteSize); buffer->data = pool.data(); @@ -2215,15 +2212,6 @@ void Renderer::uploadBatch(Batch *b) */ int bufferSize = b->vertexCount * g->sizeOfVertex(); int ibufferSize = 0; - // At this point, we need to check if the vertices byte size is 4 byte aligned or not. - // If an unaligned value is used in a shared buffer with indices, it causes problems with - // glDrawElements. We need to do a 4 byte alignment so that it can work with both - // QSGGeometry::UnsignedShortType and QSGGeometry::UnsignedIntType - int paddingBytes = 0; - if (!m_context->separateIndexBuffer()) { - paddingBytes = aligned(bufferSize, 4) - bufferSize; - bufferSize += paddingBytes; - } if (b->merged) { ibufferSize = b->indexCount * mergedIndexElemSize(); if (m_useDepthBuffer) @@ -2232,11 +2220,7 @@ void Renderer::uploadBatch(Batch *b) ibufferSize = unmergedIndexSize; } - const bool separateIndexBuffer = m_context->separateIndexBuffer(); - if (separateIndexBuffer) - map(&b->ibo, ibufferSize, true); - else - bufferSize += ibufferSize; + map(&b->ibo, ibufferSize, true); map(&b->vbo, bufferSize); if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:" @@ -2246,9 +2230,7 @@ void Renderer::uploadBatch(Batch *b) if (b->merged) { char *vertexData = b->vbo.data; char *zData = vertexData + b->vertexCount * g->sizeOfVertex(); - char *indexData = separateIndexBuffer - ? b->ibo.data - : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float)) + paddingBytes; + char *indexData = b->ibo.data; quint16 iOffset16 = 0; quint32 iOffset32 = 0; @@ -2260,8 +2242,8 @@ void Renderer::uploadBatch(Batch *b) const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe; int indicesInSet = 0; b->drawSets.reset(); - int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData; - const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data; + int drawSetIndices = 0; + const char *indexBase = b->ibo.data; b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices); while (e) { verticesInSet += e->node->geometry()->vertexCount(); @@ -2295,8 +2277,7 @@ void Renderer::uploadBatch(Batch *b) } } else { char *vboData = b->vbo.data; - char *iboData = separateIndexBuffer ? b->ibo.data - : vboData + b->vertexCount * g->sizeOfVertex() + paddingBytes; + char *iboData = b->ibo.data; Element *e = b->first; while (e) { QSGGeometry *g = e->node->geometry(); @@ -2364,9 +2345,7 @@ void Renderer::uploadBatch(Batch *b) if (!b->drawSets.isEmpty()) { if (m_uint32IndexForRhi) { - const quint32 *id = (const quint32 *)(separateIndexBuffer - ? b->ibo.data - : b->vbo.data + b->drawSets.at(0).indices); + const quint32 *id = (const quint32 *) b->ibo.data; { QDebug iDump = qDebug(); iDump << " -- Index Data, count:" << b->indexCount; @@ -2377,9 +2356,7 @@ void Renderer::uploadBatch(Batch *b) } } } else { - const quint16 *id = (const quint16 *)(separateIndexBuffer - ? b->ibo.data - : b->vbo.data + b->drawSets.at(0).indices); + const quint16 *id = (const quint16 *) b->ibo.data; { QDebug iDump = qDebug(); iDump << " -- Index Data, count:" << b->indexCount; @@ -2400,8 +2377,7 @@ void Renderer::uploadBatch(Batch *b) #endif // QT_NO_DEBUG_OUTPUT unmap(&b->vbo); - if (separateIndexBuffer) - unmap(&b->ibo, true); + unmap(&b->ibo, true); if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed..."; @@ -3061,7 +3037,7 @@ void Renderer::renderMergedBatch(const Batch *batch) // legacy (GL-only) glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); char *indexBase = nullptr; - const Buffer *indexBuf = m_context->separateIndexBuffer() ? &batch->ibo : &batch->vbo; + const Buffer *indexBuf = &batch->ibo; if (m_context->hasBrokenIndexBufferObjects()) { indexBase = indexBuf->data; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -3154,8 +3130,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only) glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); char *indexBase = nullptr; - const bool separateIndexBuffer = m_context->separateIndexBuffer(); - const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo; + const Buffer *indexBuf = &batch->ibo; if (batch->indexCount) { if (m_context->hasBrokenIndexBufferObjects()) { indexBase = indexBuf->data; @@ -3185,15 +3160,6 @@ void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only) int vOffset = 0; char *iOffset = indexBase; - // If a shared buffer is used, 4 byte alignment was done to avoid issues - // while using glDrawElements with both QSGGeometry::UnsignedShortType and - // QSGGeometry::UnsignedIntType. Here, we need to take this into account - // while calculating iOffset value to end up with the correct offset for drawing. - int vertexDataByteSize = batch->vertexCount * gn->geometry()->sizeOfVertex(); - vertexDataByteSize = aligned(vertexDataByteSize, 4); - if (!separateIndexBuffer) - iOffset += vertexDataByteSize; - QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4(); while (e) { @@ -4363,7 +4329,7 @@ void Renderer::render() if (largestVBO * 2 < m_vertexUploadPool.size()) m_vertexUploadPool.resize(largestVBO * 2); - if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size()) + if (largestIBO * 2 < m_indexUploadPool.size()) m_indexUploadPool.resize(largestIBO * 2); renderBatches(); diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp index c97f190c0a..a21d708821 100644 --- a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp +++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp @@ -130,7 +130,7 @@ void OpenGLVisualizer::visualizeBatch(Batch *b) if (b->merged) { shader->setUniformValue(shader->matrix, matrix); - const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data; + const char *dataStart = b->ibo.data; for (int ds=0; dsdrawSets.size(); ++ds) { const DrawSet &set = b->drawSets.at(ds); m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 61f7f2a689..bf28b24b3a 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -415,21 +415,6 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context) return qobject_cast(context->property(QSG_RENDERCONTEXT_PROPERTY).value()); } -bool QSGDefaultRenderContext::separateIndexBuffer() const -{ - if (m_rhi) - return true; - - // WebGL: A given WebGLBuffer object may only be bound to one of - // the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its - // lifetime. An attempt to bind a buffer object to the other - // target will generate an INVALID_OPERATION error, and the - // current binding will remain untouched. - static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0 - || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0); - return isWebGL; -} - void QSGDefaultRenderContext::preprocess() { for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) { diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index 3dfc6b1c42..5855f8dfd6 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -140,7 +140,6 @@ public: bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } int maxTextureSize() const override { return m_maxTextureSize; } - bool separateIndexBuffer() const; int msaaSampleCount() const { return m_initParams.sampleCount; } -- cgit v1.2.3 From d338d7435126523e7aa1533bb956d6e2e98fc71d Mon Sep 17 00:00:00 2001 From: Tarja Sundqvist Date: Wed, 15 Jun 2022 20:08:03 +0300 Subject: Bump version --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 0d692629d3..a0e2de0f37 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ CONFIG += warning_clean DEFINES += QT_NO_LINKED_LIST DEFINES += QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 5.15.10 +MODULE_VERSION = 5.15.11 -- cgit v1.2.3 From aeedbf923d9ed39db34e36b424602321974f2aa2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 14 Jun 2022 13:22:27 +0200 Subject: Fix rendernode example wrt stacking ...for OpenGL. This means that putting opaque content on top (which uses the opaque pass of the sg renderer) is now working as it should. Previously the custom rendering was not testing against the what was in the depth buffer, which was fine for geometry drawn in the alpha pass, but not for others. The associated bug report's example with adding a Button on top presumably triggers the latter. The software backend has no problems. The D3D12 and Metal code paths are no longer supported and will not be updated anymore. Those anyway feature various limitations, such as a lack of clipping support, so having limited stacking, depending on the content on top, is acceptable. Fixes: QTBUG-102954 Change-Id: I0ee97fb9790ee43f005b1de8226ef760c56c2573 Reviewed-by: Andy Nichols --- examples/quick/scenegraph/rendernode/openglrenderer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index a4e619bea9..80806d51d1 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -158,6 +158,10 @@ void OpenGLRenderNode::render(const RenderState *state) f->glEnable(GL_BLEND); f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + // Regardless of flags() returning DepthAwareRendering or not, + // we have to test against what's in the depth buffer already. + f->glEnable(GL_DEPTH_TEST); + // Clip support. if (state->scissorEnabled()) { f->glEnable(GL_SCISSOR_TEST); @@ -177,7 +181,7 @@ void OpenGLRenderNode::render(const RenderState *state) //! [4] QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const { - return BlendState | ScissorState | StencilState; + return BlendState | ScissorState | StencilState | DepthState; } QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const -- cgit v1.2.3 From a6bd340fa93ba351537929a3fa2ad4c00e4e8c58 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 16 Jun 2022 14:32:44 +0200 Subject: qqw: Invalidate and reinitialize the scenegraph correctly ...when the top-level window changes and AA_ShareOpenGLContext is not set. Not applicable to Qt 6, at least not to 6.4 and newer where the code in question does not exist / is different. According to the report it is not applicable to other Qt 6 versions either, so keeping this Qt 5 only. Fixes: QTBUG-100579 Change-Id: I514ce01e15ab46773db7d703a3b0cb67aa035334 Reviewed-by: Andy Nichols --- src/quickwidgets/qquickwidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index d385b6d77d..4bf0946506 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -978,11 +978,14 @@ void QQuickWidget::createFramebufferObject() } QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext(); + bool nativeContextGotRecreated = false; if (shareWindowContext && context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) { + d->invalidateRenderControl(); context->setShareContext(shareWindowContext); context->setScreen(shareWindowContext->screen()); if (!context->create()) qWarning("QQuickWidget: Failed to recreate context"); + nativeContextGotRecreated = true; // The screen may be different so we must recreate the offscreen surface too. // Unlike QOpenGLContext, QOffscreenSurface's create() does not recreate so have to destroy() first. d->offscreenSurface->destroy(); @@ -1042,6 +1045,10 @@ void QQuickWidget::createFramebufferObject() // Having one would mean create() was called and platforms that only support // a single native window were in trouble. Q_ASSERT(!d->offscreenWindow->handle()); + + // do this at the end it may trigger a recursive call + if (nativeContextGotRecreated) + d->renderControl->initialize(context); #endif } -- cgit v1.2.3 From 3af1c96d7976938b30c1e4d141a84c03d07c7a67 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 16 Jun 2022 19:06:04 +0200 Subject: Quick test lib: Account for DPR when grabbing sub-image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, the item that we are trying to grab lives in non-pixel coordinates, so we end up with a sub-image that is smaller than needed and incorrectly translated. Fixes: QTBUG-98914 Change-Id: Ic36b4d2cb0de5ef50a726c2ee3207c95d06b6a05 Reviewed-by: Tor Arne Vestbø (cherry picked from commit 47790977f53400d981d7086b8594937ad6ee8e20) Reviewed-by: Qt Cherry-pick Bot --- src/qmltest/quicktestresult.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 3310f90c2b..0ebc3f42b2 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -773,7 +773,8 @@ QObject *QuickTestResult::grabImage(QQuickItem *item) if (item && item->window()) { QQuickWindow *window = item->window(); QImage grabbed = window->grabWindow(); - QRectF rf(item->x(), item->y(), item->width(), item->height()); + const auto dpi = grabbed.devicePixelRatio(); + QRectF rf(item->x() * dpi, item->y() * dpi, item->width() * dpi, item->height() * dpi); rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height())); QObject *o = new QuickTestImageObject(grabbed.copy(rf.toAlignedRect())); QQmlEngine::setContextForObject(o, qmlContext(this)); -- cgit v1.2.3 From 470f3e76511f14d6970eaa6dc7e1589f529530bd Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 20 Jun 2022 15:50:12 +0200 Subject: Fix Qt build with Python being in path with spaces If the python executable is located in a directory containing spaces, the build failed. We must properly quote the executable path. Fixes: QTBUG-94900 Change-Id: I9645e57f69141cea700deeb8dc90acb6432952b8 Reviewed-by: Alexandru Croitor --- src/qml/configure.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/configure.pri b/src/qml/configure.pri index fcd2eacace..4506a5790d 100644 --- a/src/qml/configure.pri +++ b/src/qml/configure.pri @@ -13,7 +13,7 @@ defineTest(qtConfTest_detectPython) { } # Make tests.python.location available in configure.json. - $${1}.location = $$shell_path($$python_path) + $${1}.location = $$shell_quote($$shell_path($$python_path)) export($${1}.location) $${1}.cache += location export($${1}.cache) -- cgit v1.2.3 From ed9f94779e87a763038afbf25a9e1a44a498a3ed Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 3 May 2022 14:52:37 +0200 Subject: QML: Port QV4::CompiledData::Location to new special integer bitfield Task-number: QTBUG-99545 Change-Id: If0d6f893f2351a4146ddf125be4079b5e312f308 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 893b6ae6e890a2b8fc842d9c9cc64b9b8f34e22f) --- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 6 +- src/qml/common/qv4compileddata_p.h | 35 +++++++++--- src/qml/compiler/qqmlirbuilder.cpp | 64 ++++++++-------------- src/qml/compiler/qv4compiler.cpp | 3 +- src/qml/compiler/qv4compilerscanfunctions.cpp | 5 +- src/qml/debugger/qqmlprofiler_p.h | 2 +- src/qml/jsruntime/qv4executablecompilationunit.cpp | 8 ++- src/qml/jsruntime/qv4function.cpp | 3 +- src/qml/jsruntime/qv4profiling.cpp | 4 +- src/qml/qml/qqmlbinding.cpp | 8 ++- src/qml/qml/qqmlcomponent.cpp | 6 +- src/qml/qml/qqmlcustomparser.cpp | 4 +- src/qml/qml/qqmlobjectcreator.cpp | 12 ++-- src/qml/qml/qqmlpropertycachecreator_p.h | 4 +- src/qml/qml/qqmlpropertyvalidator.cpp | 6 +- src/qml/qml/qqmlscriptblob.cpp | 8 +-- src/qml/qml/qqmltypecompiler.cpp | 4 +- src/qml/qml/qqmltypedata.cpp | 37 +++++++------ src/qml/qml/qqmltypeloader.cpp | 4 +- src/qmltest/quicktest.cpp | 4 +- 20 files changed, 117 insertions(+), 110 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 8fe4a3061e..7f0fc7efce 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -812,9 +812,9 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth int lineNumber = 0; QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex())); - if (oldMethod && oldMethod->d()->function) { - lineNumber = oldMethod->d()->function->compiledFunction->location.line; - } + if (oldMethod && oldMethod->d()->function) + lineNumber = oldMethod->d()->function->compiledFunction->location.line(); + QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); vmeMetaObject->setVmeMethod(prop->coreIndex(), v); return true; diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 231cd16d68..ca389d2f05 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -120,18 +120,35 @@ struct TableIterator struct Location { - union { - quint32 _dummy; - quint32_le_bitfield<0, 20> line; - quint32_le_bitfield<20, 12> column; - }; - - Location() : _dummy(0) { } + Location() : m_data(QSpecialIntegerBitfieldZero) {} + Location(quint32 l, quint32 c) + { + m_data.set(l); + m_data.set(c); + Q_ASSERT(m_data.get() == l); + Q_ASSERT(m_data.get() == c); + } inline bool operator<(const Location &other) const { - return line < other.line || - (line == other.line && column < other.column); + return m_data.get() < other.m_data.get() + || (m_data.get() == other.m_data.get() + && m_data.get() < other.m_data.get()); } + + void set(quint32 line, quint32 column) + { + m_data.set(line); + m_data.set(column); + } + + quint32 line() const { return m_data.get(); } + quint32 column() const { return m_data.get(); } + +private: + using LineField = quint32_le_bitfield_member<0, 20>; + using ColumnField = quint32_le_bitfield_member<20, 12>; + + quint32_le_bitfield_union m_data; }; static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index f4f2f49783..722acc249c 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -173,9 +173,7 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons { inheritedTypeNameIndex = typeNameIndex; - location.line = loc.startLine; - location.column = loc.startColumn; - + location.set(loc.startLine, loc.startColumn); idNameIndex = idIndex; id = -1; indexOfDefaultPropertyOrAlias = -1; @@ -198,8 +196,8 @@ QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet &il QSet functionNames; for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) { Function *f = functionit.ptr; - errorLocation->startLine = f->location.line; - errorLocation->startColumn = f->location.column; + errorLocation->startLine = f->location.line(); + errorLocation->startColumn = f->location.column(); if (functionNames.contains(f->nameIndex)) return tr("Duplicate method name"); functionNames.insert(f->nameIndex); @@ -397,8 +395,7 @@ void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString import->type = QV4::CompiledData::Import::ImportScript; import->uriIndex = jsGenerator->registerString(jsfile); import->qualifierIndex = jsGenerator->registerString(module); - import->location.line = lineNumber; - import->location.column = column; + import->location.set(lineNumber, column); document->imports << import; } @@ -413,8 +410,7 @@ void ScriptDirectivesCollector::importModule(const QString &uri, const QString & import->majorVersion = vmaj; import->minorVersion = vmin; import->qualifierIndex = jsGenerator->registerString(module); - import->location.line = lineNumber; - import->location.column = column; + import->location.set(lineNumber, column); document->imports << import; } @@ -575,8 +571,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast) inlineComponent->nameIndex = registerString(ast->name.toString()); inlineComponent->objectIndex = idx; auto location = ast->firstSourceLocation(); - inlineComponent->location.line = location.startLine; - inlineComponent->location.column = location.startColumn; + inlineComponent->location.set(location.startLine, location.startColumn); _object->appendInlineComponent(inlineComponent); return false; } @@ -772,8 +767,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) import->minorVersion = -1; } - import->location.line = node->importToken.startLine; - import->location.column = node->importToken.startColumn; + import->location.set(node->importToken.startLine, node->importToken.startColumn); import->uriIndex = registerString(uri); @@ -801,8 +795,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node) return false; } - pragma->location.line = node->pragmaToken.startLine; - pragma->location.column = node->pragmaToken.startColumn; + pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn); _pragmas.append(pragma); return false; @@ -835,8 +828,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) if (enumName.at(0).isLower()) COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter")); - enumeration->location.line = node->enumToken.startLine; - enumeration->location.column = node->enumToken.startColumn; + enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn); enumeration->enumValues = New>(); @@ -855,8 +847,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range")); enumValue->value = e->value; - enumValue->location.line = e->memberToken.startLine; - enumValue->location.column = e->memberToken.startColumn; + enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn); enumeration->enumValues->append(enumValue); e = e->next; @@ -880,8 +871,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) signal->nameIndex = registerString(signalName); QQmlJS::SourceLocation loc = node->typeToken; - signal->location.line = loc.startLine; - signal->location.column = loc.startColumn; + signal->location.set(loc.startLine, loc.startColumn); signal->parameters = New >(); @@ -963,8 +953,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) property->nameIndex = registerString(propName); QQmlJS::SourceLocation loc = node->firstSourceLocation(); - property->location.line = loc.startLine; - property->location.column = loc.startColumn; + property->location.set(loc.startLine, loc.startColumn); QQmlJS::SourceLocation errorLocation; QString error; @@ -1008,8 +997,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) Function *f = New(); QQmlJS::SourceLocation loc = funDecl->identifierToken; - f->location.line = loc.startLine; - f->location.column = loc.startColumn; + f->location.set(loc.startLine, loc.startColumn); f->index = index; f->nameIndex = registerString(funDecl->name.toString()); @@ -1089,8 +1077,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::SourceLocation &first, const QQmlJ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode) { QQmlJS::SourceLocation loc = statement->firstSourceLocation(); - binding->valueLocation.line = loc.startLine; - binding->valueLocation.column = loc.startColumn; + binding->valueLocation.set(loc.startLine, loc.startColumn); binding->type = QV4::CompiledData::Binding::Type_Invalid; if (_propertyDeclaration && _propertyDeclaration->isReadOnly) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; @@ -1295,8 +1282,7 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio Binding *binding = New(); binding->propertyNameIndex = propertyNameIndex; binding->offset = nameLocation.offset; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; + binding->location.set(nameLocation.startLine, nameLocation.startColumn); binding->flags = 0; setBindingValue(binding, value, parentNode); QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); @@ -1315,8 +1301,7 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio Binding *binding = New(); binding->propertyNameIndex = propertyNameIndex; binding->offset = nameLocation.offset; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; + binding->location.set(nameLocation.startLine, nameLocation.startColumn); const Object *obj = _objects.at(objectIndex); binding->valueLocation = obj->location; @@ -1355,8 +1340,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) alias->nameIndex = registerString(propName); QQmlJS::SourceLocation loc = node->firstSourceLocation(); - alias->location.line = loc.startLine; - alias->location.column = loc.startColumn; + alias->location.set(loc.startLine, loc.startColumn); alias->propertyNameIndex = emptyStringIndex; @@ -1370,8 +1354,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) rhsLoc = node->statement->firstSourceLocation(); else rhsLoc = node->semicolonToken; - alias->referenceLocation.line = rhsLoc.startLine; - alias->referenceLocation.column = rhsLoc.startColumn; + alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn); QStringList aliasReference; @@ -1466,8 +1449,7 @@ bool IRBuilder::setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Sta COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); _object->idNameIndex = registerString(idQString); - _object->locationOfIdProperty.line = idLocation.startLine; - _object->locationOfIdProperty.column = idLocation.startColumn; + _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn); return true; } @@ -1513,10 +1495,10 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O binding = New(); binding->propertyNameIndex = propertyNameIndex; binding->offset = qualifiedIdElement->identifierToken.offset; - binding->location.line = qualifiedIdElement->identifierToken.startLine; - binding->location.column = qualifiedIdElement->identifierToken.startColumn; - binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine; - binding->valueLocation.column = qualifiedIdElement->next->identifierToken.startColumn; + binding->location.set(qualifiedIdElement->identifierToken.startLine, + qualifiedIdElement->identifierToken.startColumn); + binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine, + qualifiedIdElement->next->identifierToken.startColumn); binding->flags = 0; if (onAssignment) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 2aa7a8c891..0e15f12053 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -468,8 +468,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte currentOffset += function->nLabelInfos * sizeof(quint32); } - function->location.line = irFunction->line; - function->location.column = irFunction->column; + function->location.set(irFunction->line, irFunction->column); function->codeOffset = currentOffset; function->codeSize = irFunction->code.size(); diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index f71965ee29..82385a83fc 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -57,10 +57,7 @@ using namespace QQmlJS::AST; static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation) { - CompiledData::Location target; - target.line = astLocation.startLine; - target.column = astLocation.startColumn; - return target; + return CompiledData::Location(astLocation.startLine, astLocation.startColumn); } diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index e3de0a57a9..65e507f3ad 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -176,7 +176,7 @@ public: RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) - : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), + : Location(QQmlSourceLocation(type, obj->location.line(), obj->location.column()), url), locationType(Creating), sent(false) { unit = ref; diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index f5f76d2a9e..2e42aa4041 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -571,7 +571,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) if (!valuePtr) { QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); return nullptr; } imports[i] = valuePtr; @@ -587,7 +589,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) if (!dependentModuleUnit->resolveExport(importName)) { QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); return nullptr; } } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1fe94d3ca4..063915ca92 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -176,7 +176,8 @@ QString Function::prettyName(const Function *function, const void *code) QQmlSourceLocation Function::sourceLocation() const { - return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); + return QQmlSourceLocation( + sourceFile(), compiledFunction->location.line(), compiledFunction->location.column()); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 9f82130ba0..376ebf4527 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -50,8 +50,8 @@ FunctionLocation FunctionCall::resolveLocation() const { return FunctionLocation(m_function->name()->toQString(), m_function->executableCompilationUnit()->fileName(), - m_function->compiledFunction->location.line, - m_function->compiledFunction->location.column); + m_function->compiledFunction->location.line(), + m_function->compiledFunction->location.column()); } FunctionCallProperties FunctionCall::properties() const diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 3b5b0de91b..041c2730fa 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -353,7 +353,9 @@ public: QQmlSourceLocation sourceLocation() const override final { - return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column); + return QQmlSourceLocation( + m_compilationUnit->fileName(), m_binding->valueLocation.line(), + m_binding->valueLocation.column()); } @@ -532,8 +534,8 @@ QString QQmlBinding::expressionIdentifier() const { if (auto f = function()) { QString url = f->sourceFile(); - uint lineNumber = f->compiledFunction->location.line; - uint columnNumber = f->compiledFunction->location.column; + uint lineNumber = f->compiledFunction->location.line(); + uint columnNumber = f->compiledFunction->location.column(); return url + QString::asprintf(":%u:%u", lineNumber, columnNumber); } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index abf8e06e16..dd0ae01d80 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1436,8 +1436,10 @@ QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredP } error.setDescription(description); error.setUrl(unsetRequiredProperty.fileUrl); - error.setLine(qmlConvertSourceCoordinate(unsetRequiredProperty.location.line)); - error.setColumn(qmlConvertSourceCoordinate(unsetRequiredProperty.location.column)); + error.setLine(qmlConvertSourceCoordinate( + unsetRequiredProperty.location.line())); + error.setColumn(qmlConvertSourceCoordinate( + unsetRequiredProperty.location.column())); return error; } diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index e15d53de10..0a17daed1f 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -102,8 +102,8 @@ void QQmlCustomParser::clearErrors() void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setLine(qmlConvertSourceCoordinate(location.line)); - error.setColumn(qmlConvertSourceCoordinate(location.column)); + error.setLine(qmlConvertSourceCoordinate(location.line())); + error.setColumn(qmlConvertSourceCoordinate(location.column())); error.setDescription(description); exceptions << error; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index c1b60d13b0..03995871ff 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -827,8 +827,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; - ss.d.data()->lineNumber = binding->location.line; - ss.d.data()->columnNumber = binding->location.column; + ss.d.data()->lineNumber = binding->location.line(); + ss.d.data()->columnNumber = binding->location.column(); ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding); @@ -1135,8 +1135,8 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, { QQmlError error; error.setUrl(compilationUnit->url()); - error.setLine(qmlConvertSourceCoordinate(location.line)); - error.setColumn(qmlConvertSourceCoordinate(location.column)); + error.setLine(qmlConvertSourceCoordinate(location.line())); + error.setColumn(qmlConvertSourceCoordinate(location.column())); error.setDescription(description); errors << error; } @@ -1261,8 +1261,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo compilationUnit.data(), obj, typeName, context->url())); Q_UNUSED(typeName); // only relevant for tracing - ddata->lineNumber = obj->location.line; - ddata->columnNumber = obj->location.column; + ddata->lineNumber = obj->location.line(); + ddata->columnNumber = obj->location.column(); ddata->setImplicitDestructible(); // inline components are root objects, but their index is != 0, so we need diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index be01f23217..e68e49e99b 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -67,8 +67,8 @@ inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setLine(qmlConvertSourceCoordinate(location.line)); - error.setColumn(qmlConvertSourceCoordinate(location.column)); + error.setLine(qmlConvertSourceCoordinate(location.line())); + error.setColumn(qmlConvertSourceCoordinate(location.column())); error.setDescription(description); return error; } diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 42e30b84f1..782c0e2ee7 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -387,8 +387,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope if (binding->type == QV4::CompiledData::Binding::Type_Null) { QQmlError warning; warning.setUrl(compilationUnit->url()); - warning.setLine(qmlConvertSourceCoordinate(binding->valueLocation.line)); - warning.setColumn(qmlConvertSourceCoordinate(binding->valueLocation.column)); + warning.setLine(qmlConvertSourceCoordinate( + binding->valueLocation.line())); + warning.setColumn(qmlConvertSourceCoordinate( + binding->valueLocation.column())); warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML " "is deprecated. This will become a compile error in " "future versions of Qt.")); diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp index 0ed3c9687f..84f46f012a 100644 --- a/src/qml/qml/qqmlscriptblob.cpp +++ b/src/qml/qml/qqmlscriptblob.cpp @@ -168,8 +168,8 @@ void QQmlScriptBlob::done() QList errors = script.script->errors(); QQmlError error; error.setUrl(url()); - error.setLine(qmlConvertSourceCoordinate(script.location.line)); - error.setColumn(qmlConvertSourceCoordinate(script.location.column)); + error.setLine(qmlConvertSourceCoordinate(script.location.line())); + error.setColumn(qmlConvertSourceCoordinate(script.location.column())); error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); errors.prepend(error); setError(errors); @@ -237,8 +237,8 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointerlocation.line); - error.setColumn(import->location.column); + error.setLine(import->location.line()); + error.setColumn(import->location.column()); errors.prepend(error); // put it back on the list after filling out information. setError(errors); return; diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index 78e88b23a2..d3e9aeeec1 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -174,8 +174,8 @@ QQmlRefPointer QQmlTypeCompiler::compile() void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setLine(qmlConvertSourceCoordinate(location.line)); - error.setColumn(qmlConvertSourceCoordinate(location.column)); + error.setLine(qmlConvertSourceCoordinate(location.line())); + error.setColumn(qmlConvertSourceCoordinate(location.column())); error.setDescription(description); error.setUrl(url()); errors << error; diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 3fee4d0313..c40cbf91f9 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -203,8 +203,8 @@ bool QQmlTypeData::tryLoadFromDiskCache() Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); error.setUrl(m_importCache.baseUrl()); - error.setLine(qmlConvertSourceCoordinate(import->location.line)); - error.setColumn(qmlConvertSourceCoordinate(import->location.column)); + error.setLine(qmlConvertSourceCoordinate(import->location.line())); + error.setColumn(qmlConvertSourceCoordinate(import->location.column())); errors.prepend(error); // put it back on the list after filling out information. setError(errors); return false; @@ -324,8 +324,8 @@ void QQmlTypeData::done() QList errors = script.script->errors(); QQmlError error; error.setUrl(url()); - error.setLine(qmlConvertSourceCoordinate(script.location.line)); - error.setColumn(qmlConvertSourceCoordinate(script.location.column)); + error.setLine(qmlConvertSourceCoordinate(script.location.line())); + error.setColumn(qmlConvertSourceCoordinate(script.location.column())); error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); errors.prepend(error); setError(errors); @@ -348,8 +348,8 @@ void QQmlTypeData::done() QList errors = type.typeData ? type.typeData->errors() : QList{}; QQmlError error; error.setUrl(url()); - error.setLine(qmlConvertSourceCoordinate(type.location.line)); - error.setColumn(qmlConvertSourceCoordinate(type.location.column)); + error.setLine(qmlConvertSourceCoordinate(type.location.line())); + error.setColumn(qmlConvertSourceCoordinate(type.location.column())); error.setDescription(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(typeName.leftRef(lastDot), type.type.pendingResolutionName())); errors.prepend(error); setError(errors); @@ -364,8 +364,8 @@ void QQmlTypeData::done() QList errors = type.typeData->errors(); QQmlError error; error.setUrl(url()); - error.setLine(qmlConvertSourceCoordinate(type.location.line)); - error.setColumn(qmlConvertSourceCoordinate(type.location.column)); + error.setLine(qmlConvertSourceCoordinate(type.location.line())); + error.setColumn(qmlConvertSourceCoordinate(type.location.column())); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); @@ -383,8 +383,8 @@ void QQmlTypeData::done() QList errors = type.typeData->errors(); QQmlError error; error.setUrl(url()); - error.setLine(qmlConvertSourceCoordinate(type.location.line)); - error.setColumn(qmlConvertSourceCoordinate(type.location.column)); + error.setLine(qmlConvertSourceCoordinate(type.location.line())); + error.setColumn(qmlConvertSourceCoordinate(type.location.column())); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); @@ -696,8 +696,8 @@ void QQmlTypeData::continueLoadFromIR() Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); error.setUrl(m_importCache.baseUrl()); - error.setLine(qmlConvertSourceCoordinate(import->location.line)); - error.setColumn(qmlConvertSourceCoordinate(import->location.column)); + error.setLine(qmlConvertSourceCoordinate(import->location.line())); + error.setColumn(qmlConvertSourceCoordinate(import->location.column())); errors.prepend(error); // put it back on the list after filling out information. setError(errors); return; @@ -723,8 +723,10 @@ void QQmlTypeData::allDependenciesDone() QQmlError error; error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri)); error.setUrl(m_importCache.baseUrl()); - error.setLine(qmlConvertSourceCoordinate(import->location.line)); - error.setColumn(qmlConvertSourceCoordinate(import->location.column)); + error.setLine(qmlConvertSourceCoordinate( + import->location.line())); + error.setColumn(qmlConvertSourceCoordinate( + import->location.column())); errors.prepend(error); } } @@ -858,8 +860,8 @@ void QQmlTypeData::resolveTypes() bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference; - if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, - unresolvedRef->location.column, reportErrors, + if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line(), + unresolvedRef->location.column(), reportErrors, QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors) return; @@ -881,8 +883,7 @@ void QQmlTypeData::resolveTypes() ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; - ref.location.line = unresolvedRef->location.line; - ref.location.column = unresolvedRef->location.column; + ref.location = unresolvedRef->location; ref.needsCreation = unresolvedRef->needsCreation; m_resolvedTypes.insert(unresolvedRef.key(), ref); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 8c5f9f4c5b..d4442b9f25 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -697,8 +697,8 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); error.setUrl(m_importCache.baseUrl()); - error.setLine(qmlConvertSourceCoordinate(import->location.line)); - error.setColumn(qmlConvertSourceCoordinate(import->location.column)); + error.setLine(qmlConvertSourceCoordinate(import->location.line())); + error.setColumn(qmlConvertSourceCoordinate(import->location.column())); errors.prepend(error); // put it back on the list after filling out information. setError(errors); } diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index b2725b6846..eda7884cd8 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -330,8 +330,8 @@ private: } else { QQmlError error; error.setUrl(compilationUnit->url()); - error.setLine(binding->location.line); - error.setColumn(binding->location.column); + error.setLine(binding->location.line()); + error.setColumn(binding->location.column()); error.setDescription(QStringLiteral("the 'name' property of a TestCase must be a literal string")); result.errors << error; } -- cgit v1.2.3 From 83fce944bea6611b241318bd68e908b017b45ee1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 4 May 2022 11:47:38 +0200 Subject: QML: Port QV4::CompiledData::ParameterType to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I515aa7a605accc4b45f9dd4918dd6bfb6e116379 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 2642058df8429cd593d0c7adf45a818a42fc0d88) --- src/qml/common/qv4compileddata_p.h | 25 ++++++++++++++++++++----- src/qml/compiler/qqmlirbuilder.cpp | 12 +++++------- src/qml/qml/qqmlpropertycachecreator_p.h | 7 ++++--- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index ca389d2f05..9f3030984a 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -282,11 +282,26 @@ enum class BuiltinType : unsigned int { struct ParameterType { - union { - quint32 _dummy; - quint32_le_bitfield<0, 1> indexIsBuiltinType; - quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; - }; + void set(bool indexIsBuiltinType, quint32 typeNameIndexOrBuiltinType) + { + m_data.set(indexIsBuiltinType ? 1 : 0); + m_data.set(typeNameIndexOrBuiltinType); + } + + bool indexIsBuiltinType() const + { + return m_data.get() != 0; + } + + quint32 typeNameIndexOrBuiltinType() const + { + return m_data.get(); + } + +private: + using IndexIsBuiltinTypeField = quint32_le_bitfield_member<0, 1>; + using TypeNameIndexOrBuiltinTypeField = quint32_le_bitfield_member<1, 31>; + quint32_le_bitfield_union m_data; }; static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 722acc249c..d4d74eb59e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -106,20 +106,18 @@ bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::J bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex) { - paramType->indexIsBuiltinType = false; - paramType->typeNameIndexOrBuiltinType = 0; const QString typeName = stringGenerator->stringForIndex(typeNameIndex); auto builtinType = stringToBuiltinType(typeName); if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) { - if (typeName.isEmpty() || !typeName.at(0).isUpper()) + if (typeName.isEmpty() || !typeName.at(0).isUpper()) { + paramType->set(false, 0); return false; - paramType->indexIsBuiltinType = false; - paramType->typeNameIndexOrBuiltinType = typeNameIndex; + } Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); + paramType->set(false, typeNameIndex); } else { - paramType->indexIsBuiltinType = true; - paramType->typeNameIndexOrBuiltinType = static_cast(builtinType); Q_ASSERT(quint32(builtinType) < (1u << 31)); + paramType->set(true, static_cast(builtinType)); } return true; } diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index e68e49e99b..1287d636a5 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -633,13 +633,14 @@ template inline int QQmlPropertyCacheCreator::metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, QString *customTypeName) { - if (param.indexIsBuiltinType) { + if (param.indexIsBuiltinType()) { // built-in type - return metaTypeForPropertyType(static_cast(int(param.typeNameIndexOrBuiltinType))); + return metaTypeForPropertyType( + static_cast(param.typeNameIndexOrBuiltinType())); } // lazily resolved type - const QString typeName = stringAt(param.typeNameIndexOrBuiltinType); + const QString typeName = stringAt(param.typeNameIndexOrBuiltinType()); if (customTypeName) *customTypeName = typeName; QQmlType qmltype; -- cgit v1.2.3 From 39aade85a302b14ee8cdffae5ce5a019de724feb Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 5 May 2022 11:23:03 +0200 Subject: QML: Port QV4::CompiledData::Alias to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I554f9f903a39a83eaf601fd4fd932f685bf343d0 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 8d03b13a59a5e3866c982eb14ca2dc57b687d218) --- src/qml/common/qv4compileddata_p.h | 75 ++++++++++++++++++++++++++++---- src/qml/compiler/qqmlirbuilder.cpp | 8 ++-- src/qml/qml/qqmlobjectcreator.cpp | 12 +++-- src/qml/qml/qqmlpropertycachecreator_p.h | 27 ++++++------ src/qml/qml/qqmltypecompiler.cpp | 20 ++++----- src/qml/qml/qqmlvmemetaobject.cpp | 18 ++++---- 6 files changed, 112 insertions(+), 48 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 9f3030984a..3b34a0b7cb 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -734,20 +734,31 @@ struct RequiredPropertyExtraData { static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Alias { - enum Flags : unsigned int { +private: + using NameIndexField = quint32_le_bitfield_member<0, 29>; + using FlagsField = quint32_le_bitfield_member<29, 3>; + + // object id index (in QQmlContextData::idValues) + using TargetObjectIdField = quint32_le_bitfield_member<0, 31>; + using AliasToLocalAliasField = quint32_le_bitfield_member<31, 1>; + +public: + + enum Flag : unsigned int { IsReadOnly = 0x1, Resolved = 0x2, AliasPointsToPointerObject = 0x4 }; - union { - quint32_le_bitfield<0, 29> nameIndex; - quint32_le_bitfield<29, 3> flags; - }; + Q_DECLARE_FLAGS(Flags, Flag) + + quint32_le_bitfield_union nameIndexAndFlags; + union { quint32_le idIndex; // string index - quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) - quint32_le_bitfield<31, 1> aliasToLocalAlias; + quint32_le_bitfield_union + targetObjectIdAndAliasToLocalAlias; }; + union { quint32_le propertyNameIndex; // string index qint32_le encodedMetaPropertyIndex; @@ -756,10 +767,56 @@ struct Alias { Location location; Location referenceLocation; - bool isObjectAlias() const { - Q_ASSERT(flags & Resolved); + bool hasFlag(Flag flag) const + { + return nameIndexAndFlags.get() & flag; + } + + void setFlag(Flag flag) + { + nameIndexAndFlags.set(nameIndexAndFlags.get() | flag); + } + + void clearFlags() + { + nameIndexAndFlags.set(0); + } + + quint32 nameIndex() const + { + return nameIndexAndFlags.get(); + } + + void setNameIndex(quint32 nameIndex) + { + nameIndexAndFlags.set(nameIndex); + } + + bool isObjectAlias() const + { + Q_ASSERT(hasFlag(Resolved)); return encodedMetaPropertyIndex == -1; } + + bool isAliasToLocalAlias() const + { + return targetObjectIdAndAliasToLocalAlias.get(); + } + + void setIsAliasToLocalAlias(bool isAliasToLocalAlias) + { + targetObjectIdAndAliasToLocalAlias.set(isAliasToLocalAlias); + } + + quint32 targetObjectId() const + { + return targetObjectIdAndAliasToLocalAlias.get(); + } + + void setTargetObjectId(quint32 targetObjectId) + { + targetObjectIdAndAliasToLocalAlias.set(targetObjectId); + } }; static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index d4d74eb59e..026509aa7b 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -276,7 +276,7 @@ QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefau target = this; auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){ - return targetAlias.nameIndex == alias->nameIndex; + return targetAlias.nameIndex() == alias->nameIndex(); }); if (aliasWithSameName != target->aliases->end()) return tr("Duplicate alias name"); @@ -1330,12 +1330,12 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) { Alias *alias = New(); - alias->flags = 0; + alias->clearFlags(); if (node->isReadonlyMember) - alias->flags |= QV4::CompiledData::Alias::IsReadOnly; + alias->setFlag(QV4::CompiledData::Alias::IsReadOnly); const QString propName = node->name.toString(); - alias->nameIndex = registerString(propName); + alias->setNameIndex(registerString(propName)); QQmlJS::SourceLocation loc = node->firstSourceLocation(); alias->location.set(loc.startLine, loc.startColumn); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 03995871ff..88e82713ba 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1556,12 +1556,12 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) { const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex; const auto originalAlias = alias; - while (alias->aliasToLocalAlias) + while (alias->isAliasToLocalAlias()) alias = _compiledObject->aliasesBegin() + alias->localAliasIndex; - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved)); if (!context->idValues->wasSet()) continue; - QObject *target = context->idValues[alias->targetObjectId].data(); + QObject *target = context->idValues[alias->targetObjectId()].data(); if (!target) continue; QQmlData *targetDData = QQmlData::get(target, /*create*/false); @@ -1573,7 +1573,11 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * continue; auto it = sharedState->requiredProperties.find(targetProperty); if (it != sharedState->requiredProperties.end()) - it->aliasesToRequired.push_back(AliasToRequiredInfo {compilationUnit->stringAt(originalAlias->nameIndex), compilationUnit->finalUrl()}); + it->aliasesToRequired.push_back( + AliasToRequiredInfo { + compilationUnit->stringAt(originalAlias->nameIndex()), + compilationUnit->finalUrl() + }); } qSwap(_vmeMetaObject, vmeMetaObject); diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 1287d636a5..f97e623a90 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -390,7 +390,7 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int auto aend = obj->aliasesEnd(); for ( ; a != aend; ++a) { bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex()), ¬InRevision); if (d && d->isFinal()) return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); } @@ -438,7 +438,7 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int for ( ; a != aend; ++a) { auto flags = QQmlPropertyData::defaultSignalFlags(); - QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + QString changedSigName = stringAt(a->nameIndex()) + QLatin1String("Changed"); seenSignals.insert(changedSigName); cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); @@ -726,12 +726,12 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie auto alias = object.aliasesBegin(); auto end = object.aliasesEnd(); for ( ; alias != end; ++alias) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved)); - const int targetObjectIndex = objectForId(component, alias->targetObjectId); + const int targetObjectIndex = objectForId(component, alias->targetObjectId()); Q_ASSERT(targetObjectIndex >= 0); - if (alias->aliasToLocalAlias) + if (alias->isAliasToLocalAlias()) continue; if (alias->encodedMetaPropertyIndex == -1) @@ -798,12 +798,12 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor propertyFlags->setIsAlias(true); - if (alias.aliasToLocalAlias) { + if (alias.isAliasToLocalAlias()) { const QV4::CompiledData::Alias *lastAlias = &alias; QVarLengthArray seenAliases({lastAlias}); do { - const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId); + const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId()); Q_ASSERT(targetObjectIndex >= 0); const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); Q_ASSERT(targetObject); @@ -820,17 +820,17 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor seenAliases.append(targetAlias); lastAlias = targetAlias; - } while (lastAlias->aliasToLocalAlias); + } while (lastAlias->isAliasToLocalAlias()); return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv); } - const int targetObjectIndex = objectForId(component, alias.targetObjectId); + const int targetObjectIndex = objectForId(component, alias.targetObjectId()); Q_ASSERT(targetObjectIndex >= 0); const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); if (alias.encodedMetaPropertyIndex == -1) { - Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); + Q_ASSERT(alias.hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject)); auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex); if (!typeRef) { // Can be caused by the alias target not being a valid id or property. E.g.: @@ -902,7 +902,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor } } - propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable); + propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly)) + && writable); propertyFlags->setIsResettable(resettable); return QQmlError(); } @@ -925,7 +926,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator::appendAliasesTo auto alias = object.aliasesBegin(); auto end = object.aliasesEnd(); for ( ; alias != end; ++alias, ++aliasIndex) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved)); int type = 0; int minorVersion = 0; @@ -934,7 +935,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator::appendAliasesTo if (error.isValid()) return error; - const QString propertyName = objectContainer->stringAt(alias->nameIndex); + const QString propertyName = objectContainer->stringAt(alias->nameIndex()); if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) propertyCache->_defaultPropertyName = propertyName; diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index d3e9aeeec1..cf2907e23f 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -1054,7 +1054,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { - if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { + if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) { recordError(alias->location, tr("Circular alias reference detected")); return false; } @@ -1076,7 +1076,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, bool seenUnresolvedAlias = false; for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { - if (alias->flags & QV4::CompiledData::Alias::Resolved) + if (alias->hasFlag(QV4::CompiledData::Alias::Resolved)) continue; seenUnresolvedAlias = true; @@ -1092,8 +1092,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); Q_ASSERT(targetObject->id >= 0); - alias->targetObjectId = targetObject->id; - alias->aliasToLocalAlias = false; + alias->setTargetObjectId(targetObject->id); + alias->setIsAliasToLocalAlias(false); const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); @@ -1110,7 +1110,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlPropertyIndex propIdx; if (property.isEmpty()) { - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject); } else { QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); if (!targetCache) { @@ -1129,7 +1129,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, bool aliasPointsToOtherAlias = false; int localAliasIndex = 0; for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { - if (stringAt(targetAlias->nameIndex) == property) { + if (stringAt(targetAlias->nameIndex()) == property) { aliasPointsToOtherAlias = true; break; } @@ -1137,8 +1137,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, if (aliasPointsToOtherAlias) { if (targetObjectIndex == objectIndex) { alias->localAliasIndex = localAliasIndex; - alias->aliasToLocalAlias = true; - alias->flags |= QV4::CompiledData::Alias::Resolved; + alias->setIsAliasToLocalAlias(true); + alias->setFlag(QV4::CompiledData::Alias::Resolved); ++numResolvedAliases; continue; } @@ -1200,12 +1200,12 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, } } else { if (targetProperty->isQObject()) - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject); } } alias->encodedMetaPropertyIndex = propIdx.toEncoded(); - alias->flags |= QV4::CompiledData::Alias::Resolved; + alias->setFlag(QV4::CompiledData::Alias::Resolved); numResolvedAliases++; } diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 70e7b8bff4..711baf3122 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -231,7 +231,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId]; if (!aliasData->isObjectAlias()) { QQmlContextData *ctxt = metaObject->ctxt; - QObject *target = ctxt->idValues[aliasData->targetObjectId].data(); + QObject *target = ctxt->idValues[aliasData->targetObjectId()].data(); if (!target) return; @@ -888,18 +888,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (id < aliasCount) { const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id]; - if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty) - *reinterpret_cast(a[0]) = nullptr; + if (aliasData->hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject) + && c == QMetaObject::ReadProperty){ + *reinterpret_cast(a[0]) = nullptr; + } if (!ctxt) return -1; - while (aliasData->aliasToLocalAlias) + while (aliasData->isAliasToLocalAlias()) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; QQmlContext *context = ctxt->asQQmlContext(); QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); - QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data(); + QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId()].data(); if (!target) return -1; @@ -1260,9 +1262,9 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, const int aliasId = index - propOffset() - compiledObject->nProperties; const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId]; - while (aliasData->aliasToLocalAlias) + while (aliasData->isAliasToLocalAlias()) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; - *target = ctxt->idValues[aliasData->targetObjectId].data(); + *target = ctxt->idValues[aliasData->targetObjectId()].data(); if (!*target) return false; @@ -1290,7 +1292,7 @@ void QQmlVMEMetaObject::connectAlias(int aliasId) } endpoint->metaObject = this; - endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings); + endpoint->connect(&ctxt->idValues[aliasData->targetObjectId()].bindings); endpoint->tryConnect(); } -- cgit v1.2.3 From bf3d4aa22af56a86b6d56053551198b52323a30e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 4 May 2022 16:14:29 +0200 Subject: QML: Port QV4::CompiledData::Property to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I46f9151536ba09417d117d690d7347bd91c13e75 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 6be0db416b867d432a826556dfc00929ecd77aba) --- src/qml/common/qv4compileddata_p.h | 57 +++++++++++++++++++++++--------- src/qml/compiler/qqmlirbuilder.cpp | 14 ++++---- src/qml/qml/qqmlobjectcreator.cpp | 2 +- src/qml/qml/qqmlpropertycachecreator_p.h | 16 ++++----- src/qml/qml/qqmlvmemetaobject.cpp | 4 +-- 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 3b34a0b7cb..3abb441177 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -698,32 +698,57 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected struct Property { - quint32_le nameIndex; - union { - quint32_le_bitfield<0, 28> builtinTypeOrTypeNameIndex; - quint32_le_bitfield<28, 1> isRequired; - quint32_le_bitfield<29, 1> isBuiltinType; - quint32_le_bitfield<30, 1> isList; - quint32_le_bitfield<31, 1> isReadOnly; - }; +private: + using BuiltinTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>; + using IsRequiredField = quint32_le_bitfield_member<28, 1>; + using IsBuiltinTypeField = quint32_le_bitfield_member<29, 1>; + using IsListField = quint32_le_bitfield_member<30, 1>; + using IsReadOnlyField = quint32_le_bitfield_member<31, 1>; +public: + quint32_le nameIndex; + quint32_le_bitfield_union< + BuiltinTypeOrTypeNameIndexField, + IsRequiredField, + IsBuiltinTypeField, + IsListField, + IsReadOnlyField> data; Location location; void setBuiltinType(BuiltinType t) { - builtinTypeOrTypeNameIndex = static_cast(t); - isBuiltinType = true; + data.set(static_cast(t)); + data.set(true); } + BuiltinType builtinType() const { - if (isBuiltinType) - return static_cast(quint32(builtinTypeOrTypeNameIndex)); + if (data.get() != 0) + return BuiltinType(data.get()); return BuiltinType::InvalidBuiltin; } + void setCustomType(int nameIndex) { - builtinTypeOrTypeNameIndex = nameIndex; - isBuiltinType = false; + data.set(nameIndex); + data.set(false); } + + int customType() const + { + return data.get() ? -1 : data.get(); + } + + bool isBuiltinType() const { return data.get(); } + uint builtinTypeOrTypeNameIndex() const { return data.get(); } + + bool isList() const { return data.get(); } + void setIsList(bool isList) { data.set(isList); } + + bool isRequired() const { return data.get(); } + void setIsRequired(bool isRequired) { data.set(isRequired); } + + bool isReadOnly() const { return data.get(); } + void setIsReadOnly(bool isReadOnly) { data.set(isReadOnly); } }; static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); @@ -1218,8 +1243,8 @@ struct TypeReferenceMap : QHash auto prop = obj->propertiesBegin(); auto const propEnd = obj->propertiesEnd(); for ( ; prop != propEnd; ++prop) { - if (!prop->isBuiltinType) { - TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location); + if (!prop->isBuiltinType()) { + TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex(), prop->location); r.errorWhenNotFound = true; } } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 026509aa7b..2cf09ca27d 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -76,7 +76,7 @@ void Object::simplifyRequiredProperties() { for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) { auto requiredIt = required.find(it->nameIndex); if (requiredIt != required.end()) { - it->isRequired = true; + it->setIsRequired(true); required.erase(requiredIt); } } @@ -918,8 +918,8 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) const QStringRef &name = node->name; Property *property = New(); - property->isReadOnly = node->isReadonlyMember; - property->isRequired = node->isRequired; + property->setIsReadOnly(node->isReadonlyMember); + property->setIsRequired(node->isRequired); QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType); bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin; @@ -931,7 +931,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) property->setCustomType(registerString(memberType)); if (typeModifier == QLatin1String("list")) { - property->isList = true; + property->setIsList(true); } else if (!typeModifier.isEmpty()) { recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); return false; @@ -1077,7 +1077,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST QQmlJS::SourceLocation loc = statement->firstSourceLocation(); binding->valueLocation.set(loc.startLine, loc.startColumn); binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && _propertyDeclaration->isReadOnly) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast(statement); @@ -1306,7 +1306,7 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio binding->flags = 0; - if (_propertyDeclaration && _propertyDeclaration->isReadOnly) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; // No type name on the initializer means it must be a group property @@ -1566,7 +1566,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) { - if (property->isBuiltinType || property->isList) + if (property->isBuiltinType() || property->isList()) return false; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast(statement); if (!exprStmt) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 88e82713ba..ffbd9700ee 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1519,7 +1519,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex); // only compute stringAt if there's a chance for the lookup to succeed auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex)); - if (!property->isRequired && postHocRequired.end() == postHocIt) + if (!property->isRequired() && postHocRequired.end() == postHocIt) continue; if (postHocIt != postHocRequired.end()) postHocRequired.erase(postHocIt); diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index f97e623a90..a34e5c3433 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -556,12 +556,13 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int if (type == QV4::CompiledData::BuiltinType::Variant) propertyFlags.type = QQmlPropertyData::Flags::QVariantType; } else { - Q_ASSERT(!p->isBuiltinType); + Q_ASSERT(!p->isBuiltinType()); QQmlType qmltype; bool selfReference = false; - if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr, - nullptr, QQmlType::AnyRegistrationType, &selfReference)) { + if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex()), &qmltype, + nullptr, nullptr, nullptr, nullptr, + QQmlType::AnyRegistrationType, &selfReference)) { return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); } @@ -592,13 +593,13 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int typeIds = compilationUnit->typeIdsForComponent(); } - if (p->isList) { + if (p->isList()) { propertyType = typeIds.listId; } else { propertyType = typeIds.id; } } else { - if (p->isList) { + if (p->isList()) { propertyType = qmltype.qListTypeId(); } else { propertyType = qmltype.typeId(); @@ -606,16 +607,15 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int } } - if (p->isList) + if (p->isList()) propertyFlags.type = QQmlPropertyData::Flags::QListType; else propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; } - if (!p->isReadOnly && !p->isList) + if (!p->isReadOnly() && !p->isList()) propertyFlags.setIsWritable(true); - QString propertyName = stringAt(p->nameIndex); if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) cache->_defaultPropertyName = propertyName; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 711baf3122..11edeb0914 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -758,7 +758,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * } break; case QV4::CompiledData::BuiltinType::InvalidBuiltin: - if (property.isList) { + if (property.isList()) { // when reading from the list, we need to find the correct MetaObject, // namely this. However, obejct->metaObject might point to any MetaObject // down the inheritance hierarchy, so we need to store how far we have @@ -866,7 +866,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * writeProperty(id, *reinterpret_cast(a[0])); break; case QV4::CompiledData::BuiltinType::InvalidBuiltin: - if (property.isList) { + if (property.isList()) { // Writing such a property is not supported. Content is added through the list property // methods. } else { -- cgit v1.2.3 From 48dc55dec2402a425e5370a566b16892abf07a95 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 4 May 2022 15:26:30 +0200 Subject: QML: Port QV4::CompiledData::Binding to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I9f8bc5fa45c61f77ee95b055a3d8de001da8f8c5 Reviewed-by: Fabian Kosmale (cherry picked from commit 36ebee4e69182f0e44d87691d4740b271e1dcf38) --- src/imports/statemachine/signaltransition.cpp | 4 +- src/qml/common/qv4compileddata_p.h | 64 +++++++----- src/qml/compiler/qqmlirbuilder.cpp | 79 ++++++++------- src/qml/jsruntime/qv4executablecompilationunit.cpp | 4 +- src/qml/jsruntime/qv4executablecompilationunit_p.h | 2 +- src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmlirloader.cpp | 2 +- src/qml/qml/qqmlobjectcreator.cpp | 81 ++++++++------- src/qml/qml/qqmlpropertycachecreator.cpp | 4 +- src/qml/qml/qqmlpropertycachecreator_p.h | 20 ++-- src/qml/qml/qqmlpropertyvalidator.cpp | 76 +++++++------- src/qml/qml/qqmltypecompiler.cpp | 109 ++++++++++++--------- src/qml/types/qqmlconnections.cpp | 13 ++- src/qmlmodels/qqmllistmodel.cpp | 17 ++-- src/qmltest/quicktest.cpp | 4 +- src/quick/util/qquickpropertychanges.cpp | 35 ++++--- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 6 +- tests/auto/qml/qqmllanguage/testtypes.cpp | 2 +- .../qml/qqmltranslation/tst_qqmltranslation.cpp | 14 +-- tools/qmlcachegen/qmlcachegen.cpp | 4 +- 20 files changed, 311 insertions(+), 231 deletions(-) diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index e42c1bf368..99a9ebba95 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -166,7 +166,7 @@ void SignalTransition::connectTriggered() Q_ASSERT(m_bindings.count() == 1); const QV4::CompiledData::Binding *binding = m_bindings.at(0); - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script); QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle(); QV4::Scope scope(jsEngine); @@ -198,7 +198,7 @@ void SignalTransitionParser::verifyBindings(const QQmlRefPointertype != QV4::CompiledData::Binding::Type_Script) { + if (binding->type() != QV4::CompiledData::Binding::Type_Script) { error(binding, SignalTransition::tr("SignalTransition: script expected")); return; } diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 3abb441177..93e4c66493 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -480,7 +480,7 @@ struct Binding { quint32_le propertyNameIndex; - enum ValueType : unsigned int { + enum Type : unsigned int { Type_Invalid, Type_Boolean, Type_Number, @@ -494,7 +494,7 @@ struct Binding Type_GroupProperty }; - enum Flags : unsigned int { + enum Flag : unsigned int { IsSignalHandlerExpression = 0x1, IsSignalHandlerObject = 0x2, IsOnAssignment = 0x4, @@ -506,11 +506,20 @@ struct Binding IsCustomParserBinding = 0x100, IsFunctionExpression = 0x200 }; + Q_DECLARE_FLAGS(Flags, Flag); + + using FlagsField = quint32_le_bitfield_member<0, 16>; + using TypeField = quint32_le_bitfield_member<16, 16>; + quint32_le_bitfield_union flagsAndType; + + void clearFlags() { flagsAndType.set(0); } + void setFlag(Flag flag) { flagsAndType.set(flagsAndType.get() | flag); } + bool hasFlag(Flag flag) const { return Flags(flagsAndType.get()) & flag; } + Flags flags() const { return Flags(flagsAndType.get()); } + + void setType(Type type) { flagsAndType.set(type); } + Type type() const { return Type(flagsAndType.get()); } - union { - quint32_le_bitfield<0, 16> flags; - quint32_le_bitfield<16, 16> type; - }; union { bool b; quint32_le constantValueIndex; @@ -524,23 +533,29 @@ struct Binding Location location; Location valueLocation; + bool hasSignalHandlerBindingFlag() const + { + const Flags bindingFlags = flags(); + return (bindingFlags & IsSignalHandlerExpression || bindingFlags & IsSignalHandlerObject); + } + bool isValueBinding() const { - if (type == Type_AttachedProperty - || type == Type_GroupProperty) + switch (type()) { + case Type_AttachedProperty: + case Type_GroupProperty: return false; - if (flags & IsSignalHandlerExpression - || flags & IsSignalHandlerObject) - return false; - return true; + default: + return !hasSignalHandlerBindingFlag(); + } } - bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); } - bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); } + bool isValueBindingNoAlias() const { return isValueBinding() && !hasFlag(IsBindingToAlias); } + bool isValueBindingToAlias() const { return isValueBinding() && hasFlag(IsBindingToAlias); } bool isSignalHandler() const { - if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) { + if (hasSignalHandlerBindingFlag()) { Q_ASSERT(!isValueBinding()); Q_ASSERT(!isAttachedProperty()); Q_ASSERT(!isGroupProperty()); @@ -551,7 +566,7 @@ struct Binding bool isAttachedProperty() const { - if (type == Type_AttachedProperty) { + if (type() == Type_AttachedProperty) { Q_ASSERT(!isValueBinding()); Q_ASSERT(!isSignalHandler()); Q_ASSERT(!isGroupProperty()); @@ -562,7 +577,7 @@ struct Binding bool isGroupProperty() const { - if (type == Type_GroupProperty) { + if (type() == Type_GroupProperty) { Q_ASSERT(!isValueBinding()); Q_ASSERT(!isSignalHandler()); Q_ASSERT(!isAttachedProperty()); @@ -571,7 +586,7 @@ struct Binding return false; } - bool isFunctionExpression() const { return (flags & IsFunctionExpression); } + bool isFunctionExpression() const { return hasFlag(IsFunctionExpression); } //reverse of Lexer::singleEscape() static QString escapedString(const QString &string) @@ -616,16 +631,19 @@ struct Binding return tmp; } - bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } - bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } + bool isTranslationBinding() const + { + const Binding::Type bindingType = type(); + return bindingType == Type_Translation || bindingType == Type_TranslationById; + } + bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); } bool valueAsBoolean() const { - if (type == Type_Boolean) + if (type() == Type_Boolean) return value.b; return false; } - }; static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); @@ -1252,7 +1270,7 @@ struct TypeReferenceMap : QHash auto binding = obj->bindingsBegin(); auto const bindingEnd = obj->bindingsEnd(); for ( ; binding != bindingEnd; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) + if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) this->add(binding->propertyNameIndex, binding->location); } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 2cf09ca27d..15b333584e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -319,13 +319,17 @@ void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraDat QString Object::appendBinding(Binding *b, bool isListBinding) { const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0)); - if (!isListBinding && !bindingToDefaultProperty - && b->type != QV4::CompiledData::Binding::Type_GroupProperty - && b->type != QV4::CompiledData::Binding::Type_AttachedProperty - && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + if (!isListBinding + && !bindingToDefaultProperty + && b->type() != QV4::CompiledData::Binding::Type_GroupProperty + && b->type() != QV4::CompiledData::Binding::Type_AttachedProperty + && !b->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { Binding *existing = findBinding(b->propertyNameIndex); - if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment)) + if (existing + && existing->isValueBinding() == b->isValueBinding() + && !existing->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { return tr("Property value set multiple times"); + } } if (bindingToDefaultProperty) insertSorted(b); @@ -1076,24 +1080,24 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST { QQmlJS::SourceLocation loc = statement->firstSourceLocation(); binding->valueLocation.set(loc.startLine, loc.startColumn); - binding->type = QV4::CompiledData::Binding::Type_Invalid; + binding->setType(QV4::CompiledData::Binding::Type_Invalid); if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration); QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast(statement); if (exprStmt) { QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression; if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast(expr)) { - binding->type = QV4::CompiledData::Binding::Type_String; + binding->setType(QV4::CompiledData::Binding::Type_String); binding->stringIndex = registerString(lit->value.toString()); } else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->setType(QV4::CompiledData::Binding::Type_Boolean); binding->value.b = true; } else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->setType(QV4::CompiledData::Binding::Type_Boolean); binding->value.b = false; } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast(expr)) { - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value)); } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast(expr)) { if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast(call->base)) { @@ -1102,21 +1106,21 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST // below. } } else if (QQmlJS::AST::cast(expr)) { - binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression; + binding->setFlag(QV4::CompiledData::Binding::IsFunctionExpression); } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast(expr)) { if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast(unaryMinus->expression)) { - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value)); } } else if (QQmlJS::AST::cast(expr)) { - binding->type = QV4::CompiledData::Binding::Type_Null; + binding->setType(QV4::CompiledData::Binding::Type_Null); binding->value.nullMarker = 0; } } // Do binding instead - if (binding->type == QV4::CompiledData::Binding::Type_Invalid) { - binding->type = QV4::CompiledData::Binding::Type_Script; + if (binding->type() == QV4::CompiledData::Binding::Type_Invalid) { + binding->setType(QV4::CompiledData::Binding::Type_Script); CompiledFunctionOrExpression *expr = New(); expr->node = statement; @@ -1172,7 +1176,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg if (args) return; // too many arguments, stop - binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->setType(QV4::CompiledData::Binding::Type_Translation); binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData); } else if (base == QLatin1String("qsTrId")) { QV4::CompiledData::TranslationData translationData; @@ -1205,7 +1209,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg if (args) return; // too many arguments, stop - binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->setType(QV4::CompiledData::Binding::Type_TranslationById); binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData); } else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) { if (!args || !args->expression) @@ -1222,7 +1226,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg if (args) return; // too many arguments, stop - binding->type = QV4::CompiledData::Binding::Type_String; + binding->setType(QV4::CompiledData::Binding::Type_String); binding->stringIndex = jsGenerator->registerString(str.toString()); } else if (base == QLatin1String("QT_TRANSLATE_NOOP")) { if (!args || !args->expression) @@ -1243,7 +1247,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg if (args) return; // too many arguments, stop - binding->type = QV4::CompiledData::Binding::Type_String; + binding->setType(QV4::CompiledData::Binding::Type_String); binding->stringIndex = jsGenerator->registerString(str.toString()); } } @@ -1281,7 +1285,7 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio binding->propertyNameIndex = propertyNameIndex; binding->offset = nameLocation.offset; binding->location.set(nameLocation.startLine, nameLocation.startColumn); - binding->flags = 0; + binding->clearFlags(); setBindingValue(binding, value, parentNode); QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); if (!error.isEmpty()) { @@ -1304,21 +1308,21 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio const Object *obj = _objects.at(objectIndex); binding->valueLocation = obj->location; - binding->flags = 0; + binding->clearFlags(); if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration); // No type name on the initializer means it must be a group property if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex) - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + binding->setType(Binding::Type_GroupProperty); else - binding->type = QV4::CompiledData::Binding::Type_Object; + binding->setType(Binding::Type_Object); if (isOnAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->setFlag(Binding::IsOnAssignment); if (isListItem) - binding->flags |= QV4::CompiledData::Binding::IsListItem; + binding->setFlag(Binding::IsListItem); binding->value.objectIndex = objectIndex; QString error = bindingsTarget()->appendBinding(binding, isListItem); @@ -1497,15 +1501,19 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O qualifiedIdElement->identifierToken.startColumn); binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine, qualifiedIdElement->next->identifierToken.startColumn); - binding->flags = 0; + binding->location.set(qualifiedIdElement->identifierToken.startLine, + qualifiedIdElement->identifierToken.startColumn); + binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine, + qualifiedIdElement->next->identifierToken.startColumn); + binding->clearFlags(); if (onAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->setFlag(QV4::CompiledData::Binding::IsOnAssignment); if (isAttachedProperty) - binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; + binding->setType(QV4::CompiledData::Binding::Type_AttachedProperty); else - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + binding->setType(QV4::CompiledData::Binding::Type_GroupProperty); int objIndex = 0; if (!defineQMLObject(&objIndex, nullptr, QQmlJS::SourceLocation(), nullptr, nullptr)) @@ -1842,7 +1850,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding continue; QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast(bindingPtr); *bindingToWrite = *b; - if (b->type == QV4::CompiledData::Binding::Type_Script) + if (b->type() == QV4::CompiledData::Binding::Type_Script) bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex); bindingPtr += sizeof(QV4::CompiledData::Binding); } @@ -1952,7 +1960,7 @@ bool JSCodeGen::compileComponent(int contextObject) if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { Q_ASSERT(obj->bindingCount() == 1); const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object); contextObject = componentBinding->value.objectIndex; } for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) @@ -1980,11 +1988,12 @@ bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int s } for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { - if (binding->type < QV4::CompiledData::Binding::Type_Object) + const Binding::Type bindingType = binding->type(); + if (bindingType < QV4::CompiledData::Binding::Type_Object) continue; int target = binding->value.objectIndex; - int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; + int scope = bindingType == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) return false; diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 2e42aa4041..972d0c0ce2 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -862,7 +862,7 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const { using namespace CompiledData; - switch (binding->type) { + switch (binding->type()) { case Binding::Type_Script: case Binding::Type_String: return stringAt(binding->stringIndex); @@ -910,7 +910,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind QString ExecutableCompilationUnit::bindingValueAsScriptString( const CompiledData::Binding *binding) const { - return (binding->type == CompiledData::Binding::Type_String) + return (binding->type() == CompiledData::Binding::Type_String) ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) : bindingValueAsString(binding); } diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 96a3678d1f..8d3f2bc4ee 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -282,7 +282,7 @@ public: QString bindingValueAsScriptString(const CompiledData::Binding *binding) const; double bindingValueAsNumber(const CompiledData::Binding *binding) const { - if (binding->type != CompiledData::Binding::Type_Number) + if (binding->type() != CompiledData::Binding::Type_Number) return 0.0; return constants[binding->value.constantValueIndex].doubleValue(); } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5c6a7b1f37..a7320a1835 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1812,7 +1812,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointerbindingTable(); for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) { const QQmlPropertyData *property = propertyData.at(i); - if (property && binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) + if (property && binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)) deferData->bindings.insert(property->coreIndex(), binding); } diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp index 609e01f8e3..7805f014f8 100644 --- a/src/qml/qml/qqmlirloader.cpp +++ b/src/qml/qml/qqmlirloader.cpp @@ -108,7 +108,7 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali QmlIR::Binding *b = pool->New(); *static_cast(b) = serializedObject->bindingTable()[i]; object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { + if (b->type() == QV4::CompiledData::Binding::Type_Script) { functionIndices.append(b->value.compiledScriptIndex); b->value.compiledScriptIndex = functionIndices.count() - 1; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index ffbd9700ee..0d6fbe6f36 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -280,7 +280,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex, if (binding) { Q_ASSERT(qmlProperty); - Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding); + Q_ASSERT(binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)); QQmlListProperty savedList; qSwap(_currentList, savedList); @@ -342,7 +342,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const int propertyType = property->propType(); if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) { + if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) { propertyType = QMetaType::Int; } else { // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. @@ -356,18 +356,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const auto assertOrNull = [&](bool ok) { - Q_ASSERT(ok || binding->type == QV4::CompiledData::Binding::Type_Null); + Q_ASSERT(ok || binding->type() == QV4::CompiledData::Binding::Type_Null); Q_UNUSED(ok); }; - auto assertType = [&](QV4::CompiledData::Binding::ValueType type) + auto assertType = [&](QV4::CompiledData::Binding::Type type) { - Q_ASSERT(binding->type == type || binding->type == QV4::CompiledData::Binding::Type_Null); + Q_ASSERT(binding->type()== type || binding->type() == QV4::CompiledData::Binding::Type_Null); Q_UNUSED(type); }; if (property->isQObject()) { - if (binding->type == QV4::CompiledData::Binding::Type_Null) { + if (binding->type() == QV4::CompiledData::Binding::Type_Null) { QObject *value = nullptr; const bool ok = property->writeProperty(_qobject, &value, propertyWriteFlags); Q_ASSERT(ok); @@ -378,7 +378,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const switch (propertyType) { case QMetaType::QVariant: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { + if (binding->type() == QV4::CompiledData::Binding::Type_Number) { double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) == n) { if (property->isVarProperty()) { @@ -396,14 +396,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const property->writeProperty(_qobject, &value, propertyWriteFlags); } } - } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + } else if (binding->type() == QV4::CompiledData::Binding::Type_Boolean) { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromBoolean(binding->valueAsBoolean())); } else { QVariant value(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); } - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + } else if (binding->type() == QV4::CompiledData::Binding::Type_Null) { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::nullValue()); } else { @@ -659,18 +659,24 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } else if (property->propType() == qMetaTypeId()) { QJSValue value; - if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Boolean: value = QJSValue(binding->valueAsBoolean()); - } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = compilationUnit->bindingValueAsNumber(binding); - if (double(int(n)) == n) { + break; + case QV4::CompiledData::Binding::Type_Number: { + const double n = compilationUnit->bindingValueAsNumber(binding); + if (double(int(n)) == n) value = QJSValue(int(n)); - } else + else value = QJSValue(n); - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + break; + } + case QV4::CompiledData::Binding::Type_Null: value = QJSValue::NullValue; - } else { + break; + default: value = QJSValue(compilationUnit->bindingValueAsString(binding)); + break; } property->writeProperty(_qobject, &value, propertyWriteFlags); break; @@ -717,8 +723,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used - idBinding.flags = 0; - idBinding.type = QV4::CompiledData::Binding::Type_String; + idBinding.clearFlags(); + idBinding.setType(QV4::CompiledData::Binding::Type_String); idBinding.stringIndex = _compiledObject->idNameIndex; idBinding.location = _compiledObject->location; // ### setPropertyValue(idProperty, &idBinding); @@ -772,10 +778,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) } - if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) + if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding)) continue; - if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) { + if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)) { if (!applyDeferredBindings) continue; } else { @@ -803,7 +809,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex); Q_ASSERT(tr); @@ -826,11 +833,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (bindingProperty && bindingProperty->propType() == qMetaTypeId()) { QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding), context->asQQmlContext(), _scopeObject); - ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; + ss.d.data()->bindingId = bindingType == QV4::CompiledData::Binding::Type_Script + ? binding->value.compiledScriptIndex + : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line(); ss.d.data()->columnNumber = binding->location.column(); - ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; - ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; + ss.d.data()->isStringLiteral = bindingType == QV4::CompiledData::Binding::Type_String; + ss.d.data()->isNumberLiteral = bindingType == QV4::CompiledData::Binding::Type_Number; ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding); QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | @@ -842,7 +851,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper } QObject *createdSubObject = nullptr; - if (binding->type == QV4::CompiledData::Binding::Type_Object) { + if (bindingType == QV4::CompiledData::Binding::Type_Object) { createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget); if (!createdSubObject) return false; @@ -851,7 +860,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (!bindingProperty) // ### error return true; - if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); + if (bindingType == QV4::CompiledData::Binding::Type_GroupProperty) { const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex); if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) { @@ -892,13 +902,15 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper } } - if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + if (_ddata->hasBindingBit(bindingProperty->coreIndex()) + && !(bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); - if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { + if (bindingType == QV4::CompiledData::Binding::Type_Script + || binding->isTranslationBinding()) { + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); @@ -954,8 +966,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper return true; } - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { + if (bindingType == QV4::CompiledData::Binding::Type_Object) { + if (bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) { // ### determine value source and interceptor casts ahead of time. QQmlType type = qmlTypeForObject(createdSubObject); Q_ASSERT(type.isValid()); @@ -1013,7 +1025,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper } // Assigning object to signal property? - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) { if (!bindingProperty->isFunction()) { recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject))); return false; @@ -1308,9 +1320,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index); const QV4::CompiledData::Binding *binding = obj->bindingTable(); for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) { + if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding)) bindings << binding; - } } customParser->applyBindings(instance, compilationUnit.data(), bindings); diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index e8f76f2747..65befa073d 100644 --- a/src/qml/qml/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -110,8 +110,10 @@ QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencing bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() { - if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty) + if (!instantiatingBinding + || instantiatingBinding->type() != QV4::CompiledData::Binding::Type_GroupProperty) { return true; + } Q_ASSERT(referencingObjectIndex >= 0); Q_ASSERT(referencingObjectPropertyCache); diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index a34e5c3433..10e1231e83 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -234,7 +234,8 @@ inline QQmlError QQmlPropertyCacheCreator::buildMetaObjectRecur auto binding = obj->bindingsBegin(); auto end = obj->bindingsEnd(); for ( ; binding != end; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + if (binding->type() == QV4::CompiledData::Binding::Type_Object + && (binding->flags() & QV4::CompiledData::Binding::IsOnAssignment)) { // If the on assignment is inside a group property, we need to distinguish between QObject based // group properties and value type group properties. For the former the base type is derived from // the property that references us, for the latter we only need a meta-object on the referencing object @@ -280,7 +281,7 @@ inline QQmlError QQmlPropertyCacheCreator::buildMetaObjectRecur auto binding = obj->bindingsBegin(); auto end = obj->bindingsEnd(); for ( ; binding != end; ++binding) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); // Binding to group property where we failed to look up the type of the @@ -779,12 +780,15 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl auto binding = object.bindingsBegin(); auto end = object.bindingsEnd(); for (; binding != end; ++binding) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Object: + case QV4::CompiledData::Binding::Type_AttachedProperty: + case QV4::CompiledData::Binding::Type_GroupProperty: + collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); + break; + default: + break; + } } } diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 782c0e2ee7..8bb95045a4 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -107,7 +107,7 @@ QVector QQmlPropertyValidator::validateObject( if (obj->flags & QV4::CompiledData::Object::IsComponent && !(obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)) { Q_ASSERT(obj->nBindings == 1); const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object); return validateObject(componentBinding->value.objectIndex, componentBinding); } @@ -131,7 +131,7 @@ QVector QQmlPropertyValidator::validateObject( if (!binding->isGroupProperty()) continue; - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) continue; if (populatingValueTypeGroupProperty) { @@ -160,9 +160,11 @@ QVector QQmlPropertyValidator::validateObject( binding = obj->bindingTable(); for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { QString name = stringAt(binding->propertyNameIndex); + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { customBindings << binding; continue; @@ -175,13 +177,14 @@ QVector QQmlPropertyValidator::validateObject( } bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; + bool isGroupProperty = instantiatingBinding + && instantiatingBinding->type() == QV4::CompiledData::Binding::Type_GroupProperty; bool notInRevision = false; QQmlPropertyData *pd = nullptr; if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) { pd = propertyResolver.signal(name, ¬InRevision); } else { pd = propertyResolver.property(name, ¬InRevision, @@ -218,11 +221,11 @@ QVector QQmlPropertyValidator::validateObject( return recordError(binding->location, tr("Invalid attached object assignment")); } - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + if (binding->type() >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { const bool populatingValueTypeGroupProperty = pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); + && !binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment); const QVector subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, populatingValueTypeGroupProperty); @@ -231,11 +234,12 @@ QVector QQmlPropertyValidator::validateObject( } // Signal handlers were resolved and checked earlier in the signal handler conversion pass. - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerExpression) + || binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject)) { continue; + } - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) { if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { return recordError(binding->location, tr("Attached properties cannot be used here")); } @@ -249,15 +253,15 @@ QVector QQmlPropertyValidator::validateObject( if (!pd->isWritable() && !pd->isQList() && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + && !(bindingFlags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) ) { - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) + if (assigningToGroupProperty && bindingType < QV4::CompiledData::Binding::Type_Object) return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { + if (!pd->isQList() && (bindingFlags & QV4::CompiledData::Binding::IsListItem)) { QString error; if (pd->propType() == qMetaTypeId()) error = tr( "Cannot assign multiple values to a script property"); @@ -268,7 +272,7 @@ QVector QQmlPropertyValidator::validateObject( if (!bindingToDefaultProperty && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) && assigningToGroupProperty) { QV4::CompiledData::Location loc = binding->valueLocation; if (loc < (*assignedGroupProperty)->valueLocation) @@ -279,11 +283,11 @@ QVector QQmlPropertyValidator::validateObject( return recordError(loc, tr("Cannot assign a value directly to a grouped property")); } - if (binding->type < QV4::CompiledData::Binding::Type_Script) { + if (bindingType < QV4::CompiledData::Binding::Type_Script) { QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding); if (bindingError.isValid()) return recordError(bindingError); - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { + } else if (bindingType == QV4::CompiledData::Binding::Type_Object) { QQmlError bindingError = validateObjectBinding(pd, name, binding); if (bindingError.isValid()) return recordError(bindingError); @@ -366,7 +370,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope QQmlError noError; if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) return noError; QString value = compilationUnit->bindingValueAsString(binding); @@ -384,7 +388,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } auto warnOrError = [&](const QString &error) { - if (binding->type == QV4::CompiledData::Binding::Type_Null) { + if (binding->type() == QV4::CompiledData::Binding::Type_Null) { QQmlError warning; warning.setUrl(compilationUnit->url()); warning.setLine(qmlConvertSourceCoordinate( @@ -400,6 +404,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope return qQmlCompileError(binding->valueLocation, error); }; + const QV4::CompiledData::Binding::Type bindingType = binding->type(); switch (property->propType()) { case QMetaType::QVariant: break; @@ -416,19 +421,17 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } break; case QMetaType::QByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + if (bindingType != QV4::CompiledData::Binding::Type_String) return warnOrError(tr("Invalid property assignment: byte array expected")); - } } break; case QMetaType::QUrl: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + if (bindingType != QV4::CompiledData::Binding::Type_String) return warnOrError(tr("Invalid property assignment: url expected")); - } } break; case QMetaType::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { + if (bindingType == QV4::CompiledData::Binding::Type_Number) { double d = compilationUnit->bindingValueAsNumber(binding); if (double(uint(d)) == d) return noError; @@ -437,7 +440,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } break; case QMetaType::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { + if (bindingType == QV4::CompiledData::Binding::Type_Number) { double d = compilationUnit->bindingValueAsNumber(binding); if (double(int(d)) == d) return noError; @@ -446,13 +449,13 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } break; case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number expected")); } } break; case QMetaType::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number expected")); } } @@ -540,7 +543,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } break; case QMetaType::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + if (bindingType != QV4::CompiledData::Binding::Type_Boolean) { return warnOrError(tr("Invalid property assignment: boolean expected")); } } @@ -596,12 +599,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope default: { // generate single literal value assignment to a list property if required if (property->propType() == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); } break; } else if (property->propType() == qMetaTypeId >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); + bool ok = (bindingType == QV4::CompiledData::Binding::Type_Number); if (ok) { double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) != n) @@ -611,12 +614,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope return warnOrError(tr("Invalid property assignment: int or array of ints expected")); break; } else if (property->propType() == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + if (bindingType != QV4::CompiledData::Binding::Type_Boolean) { return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); } break; } else if (property->propType() == qMetaTypeId >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + if (bindingType != QV4::CompiledData::Binding::Type_String) { return warnOrError(tr("Invalid property assignment: url or array of urls expected")); } break; @@ -630,7 +633,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope } else if (property->propType() == qMetaTypeId()) { break; } else if (property->isQObject() - && binding->type == QV4::CompiledData::Binding::Type_Null) { + && bindingType == QV4::CompiledData::Binding::Type_Null) { break; } @@ -692,8 +695,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert { QQmlError noError; - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); + if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Object); bool isValueSource = false; bool isPropertyInterceptor = false; @@ -742,7 +745,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert } } return noError; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { + } else if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject) + && property->isFunction()) { return noError; } else if (isPrimitiveType(propType)) { auto typeName = QString::fromUtf8(QMetaType::typeName(propType)); diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index cf2907e23f..c8f0843493 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -335,7 +335,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { QString propertyName = stringAt(binding->propertyNameIndex); // Attached property? - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedType(binding->propertyNameIndex); QQmlType type = typeRef ? typeRef->type : QQmlType(); @@ -438,13 +439,13 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } // Binding object to signal means connect the signal to the object's default method. - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; + if (bindingType == QV4::CompiledData::Binding::Type_Object) { + binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject); continue; } - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - if (binding->type < QV4::CompiledData::Binding::Type_Script) { + if (bindingType != QV4::CompiledData::Binding::Type_Script) { + if (bindingType < QV4::CompiledData::Binding::Type_Script) { COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)")); } else { COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment")); @@ -489,7 +490,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } foe->node = functionDeclaration; binding->propertyNameIndex = compiler->registerString(propertyName); - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; + binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerExpression); } return true; } @@ -513,11 +514,13 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() QQmlPropertyResolver resolver(propertyCache); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) { continue; + } - if (binding->type != QV4::CompiledData::Binding::Type_Script) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; const QString propertyName = stringAt(binding->propertyNameIndex); @@ -548,10 +551,10 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) { COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); } - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue)); // binding->setNumberValueInternal((double)enumValue); - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum); return true; } @@ -561,10 +564,13 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, if (!prop->isEnum() && !isIntProp) return true; - if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); + if (!prop->isWritable() + && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) { + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property") + .arg(stringAt(binding->propertyNameIndex))); + } - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script); const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); if (!string.constData()->isUpper()) return true; @@ -694,15 +700,21 @@ void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool if (!annotateScriptBindings) annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Script: + if (annotateScriptBindings) { + binding->stringIndex = compiler->registerString( + compiler->bindingAsString(obj, binding->value.compiledScriptIndex)); + } + break; + case QV4::CompiledData::Binding::Type_Object: + case QV4::CompiledData::Binding::Type_AttachedProperty: + case QV4::CompiledData::Binding::Type_GroupProperty: scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings); - continue; - } else if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - if (!annotateScriptBindings) - continue; - const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); + break; + default: + break; + } } } @@ -731,7 +743,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases() bool notInRevision = false; QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (pd && pd->isAlias()) - binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; + binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias); } } } @@ -758,7 +770,7 @@ void QQmlScriptStringScanner::scan() QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; bool notInRevision = false; QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; @@ -804,9 +816,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) + if (binding->type() != QV4::CompiledData::Binding::Type_Object) continue; - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject)) continue; const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); @@ -871,7 +883,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI QmlIR::Binding *syntheticBinding = pool->New(); *syntheticBinding = *binding; - syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; + syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object); QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); Q_ASSERT(error.isEmpty()); Q_UNUSED(error); @@ -932,7 +944,7 @@ bool QQmlComponentAndAliasResolver::resolve() COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); } - if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) + if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object) COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); // For the root object, we are going to collect ids/aliases and resolve them for as a separate @@ -1000,13 +1012,16 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) return true; for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - if (!collectIdsAndAliases(binding->value.objectIndex)) - return false; + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Object: + case QV4::CompiledData::Binding::Type_AttachedProperty: + case QV4::CompiledData::Binding::Type_GroupProperty: + if (!collectIdsAndAliases(binding->value.objectIndex)) + return false; + break; + default: + break; + } } return true; @@ -1241,7 +1256,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { Q_ASSERT(obj->bindingCount() == 1); const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object); return scanObject(componentBinding->value.objectIndex); } @@ -1279,16 +1294,16 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) QString name = stringAt(binding->propertyNameIndex); if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding); obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; continue; } } else if (QmlIR::IRBuilder::isSignalPropertyName(name) && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding); continue; } } @@ -1307,7 +1322,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) bool seenSubObjectWithId = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + if (binding->type() >= QV4::CompiledData::Binding::Type_Object + && (pd || binding->isAttachedProperty())) { qSwap(_seenObjectWithId, seenSubObjectWithId); const bool subObjectValid = scanObject(binding->value.objectIndex); qSwap(_seenObjectWithId, seenSubObjectWithId); @@ -1316,21 +1332,24 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) _seenObjectWithId |= seenSubObjectWithId; } - if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty + if (!seenSubObjectWithId + && binding->type() != QV4::CompiledData::Binding::Type_GroupProperty && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { - binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; + binding->setFlag(QV4::CompiledData::Binding::IsDeferredBinding); obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; } - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) { continue; + } if (!pd) { if (customParser) { obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding); } } } diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index e4e5b85872..14e57ed5ee 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -247,17 +247,20 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointertype >= QV4::CompiledData::Binding::Type_Object) { + if (binding->type() == QV4::CompiledData::Binding::Type_Script) + continue; + + if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex); if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty()) error(binding, QQmlConnections::tr("Connections: nested objects not allowed")); else error(binding, QQmlConnections::tr("Connections: syntax error")); return; - } if (binding->type != QV4::CompiledData::Binding::Type_Script) { - error(binding, QQmlConnections::tr("Connections: script expected")); - return; } + + error(binding, QQmlConnections::tr("Connections: script expected")); + return; } } @@ -352,7 +355,7 @@ void QQmlConnections::connectSignalsToBindings() QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script); QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex); QQmlProperty prop(target, propName); diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp index 33668a39ec..f9815d3cbb 100644 --- a/src/qmlmodels/qqmllistmodel.cpp +++ b/src/qmlmodels/qqmllistmodel.cpp @@ -2719,7 +2719,7 @@ void QQmlListModel::sync() bool QQmlListModelParser::verifyProperty(const QQmlRefPointer &compilationUnit, const QV4::CompiledData::Binding *binding) { - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); @@ -2747,7 +2747,7 @@ bool QQmlListModelParser::verifyProperty(const QQmlRefPointertype == QV4::CompiledData::Binding::Type_Script) { + } else if (binding->type() == QV4::CompiledData::Binding::Type_Script) { QString scriptStr = compilationUnit->bindingValueAsScriptString(binding); if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { QByteArray script = scriptStr.toUtf8(); @@ -2770,7 +2770,8 @@ bool QQmlListModelParser::applyProperty( const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); bool roleSet = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + if (bindingType >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); @@ -2803,13 +2804,13 @@ bool QQmlListModelParser::applyProperty( value = QVariant::fromValue(binding); } else if (binding->evaluatesToString()) { value = compilationUnit->bindingValueAsString(binding); - } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { + } else if (bindingType == QV4::CompiledData::Binding::Type_Number) { value = compilationUnit->bindingValueAsNumber(binding); - } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + } else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) { value = binding->valueAsBoolean(); - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + } else if (bindingType == QV4::CompiledData::Binding::Type_Null) { value = QVariant::fromValue(nullptr); - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { + } else if (bindingType == QV4::CompiledData::Binding::Type_Script) { QString scriptStr = compilationUnit->bindingValueAsScriptString(binding); if (definesEmptyList(scriptStr)) { const ListLayout::Role &role = model->getOrCreateListRole(elementName); @@ -2872,7 +2873,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointertype != QV4::CompiledData::Binding::Type_Object) + if (binding->type() != QV4::CompiledData::Binding::Type_Object) continue; setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); } diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index eda7884cd8..40f957ae0d 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -325,7 +325,7 @@ private: // Look for override of name in this type for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1String("name")) { - if (binding->type == QV4::CompiledData::Binding::Type_String) { + if (binding->type() == QV4::CompiledData::Binding::Type_String) { result.testCaseName = compilationUnit->stringAt(binding->stringIndex); } else { QQmlError error; @@ -355,7 +355,7 @@ private: } for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object) { + if (binding->type() == QV4::CompiledData::Binding::Type_Object) { const Object *child = compilationUnit->objectAt(binding->value.objectIndex); result << enumerateTestCases(compilationUnit, child); } diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index fd11dfa20c..76a35564b5 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -239,18 +239,22 @@ public: void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer &compilationUnit, const QV4::CompiledData::Binding *binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - error(compilationUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects.")); - return; - } - - if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty - || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Object: + error(compilationUnit->objectAt(binding->value.objectIndex), + QQuickPropertyChanges::tr( + "PropertyChanges does not support creating state-specific objects.")); + break; + case QV4::CompiledData::Binding::Type_GroupProperty: + case QV4::CompiledData::Binding::Type_AttachedProperty: { const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex); const QV4::CompiledData::Binding *subBinding = subObj->bindingTable(); - for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) { + for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) verifyList(compilationUnit, subBinding); - } + break; + } + default: + break; } } @@ -273,8 +277,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, QString propertyName = propertyPrefix + compilationUnit->stringAt(binding->propertyNameIndex); - if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty - || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_GroupProperty: + case QV4::CompiledData::Binding::Type_AttachedProperty: { QString pre = propertyName + QLatin1Char('.'); const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex); const QV4::CompiledData::Binding *subBinding = subObj->bindingTable(); @@ -283,6 +288,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, } return; } + default: + break; + } if (propertyName.count() >= 3 && propertyName.at(0) == QLatin1Char('o') && @@ -299,7 +307,8 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, } } - if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) { + if (binding->type() == QV4::CompiledData::Binding::Type_Script + || binding->isTranslationBinding()) { QUrl url = QUrl(); int line = -1; int column = -1; @@ -323,7 +332,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, } QVariant var; - switch (binding->type) { + switch (binding->type()) { case QV4::CompiledData::Binding::Type_Script: case QV4::CompiledData::Binding::Type_Translation: case QV4::CompiledData::Binding::Type_TranslationById: diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 3c59f7bd56..773b826e62 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -346,7 +346,7 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Script); QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0)); QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); @@ -374,7 +374,7 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(2)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number); QCOMPARE(reinterpret_cast(testUnit->constants()) [obj->bindingTable()->value.constantValueIndex].doubleValue(), @@ -419,7 +419,7 @@ void tst_qmldiskcache::registerImportForImplicitComponent() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Object); const QV4::CompiledData::Object *implicitComponent = qmlUnit->objectAt(obj->bindingTable()->value.objectIndex); QCOMPARE(testUnit->stringAtInternal(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType.elementName()); diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 31a4135d89..df9d1401a9 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -174,7 +174,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointertype != QV4::CompiledData::Binding::Type_Script) { + if (binding->type() != QV4::CompiledData::Binding::Type_Script) { error(binding, QStringLiteral("Custom parser invoked with the wrong property value. Expected script that evaluates to enum")); return; } diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index a75a00bd01..b6e0b00cc4 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -96,13 +96,13 @@ void tst_qqmltranslation::translation() const bool expectCompiledTranslation = compiledTranslations.contains(propertyName); if (expectCompiledTranslation) { - if (binding->type != QV4::CompiledData::Binding::Type_Translation) + if (binding->type() != QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation)); + QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_Translation); } else { - if (binding->type == QV4::CompiledData::Binding::Type_Translation) + if (binding->type() == QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation"; - QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); + QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation); } } } @@ -148,11 +148,11 @@ void tst_qqmltranslation::idTranslation() for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); if (propertyName == "idTranslation") { - if (binding->type != QV4::CompiledData::Binding::Type_TranslationById) + if (binding->type() != QV4::CompiledData::Binding::Type_TranslationById) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById)); + QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_TranslationById); } else { - QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); + QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation); } } } diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 9192b26555..a6b4e299b9 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -135,7 +135,7 @@ static void annotateListElements(QmlIR::Document *document) 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) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex)); } @@ -146,7 +146,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc, { for (QmlIR::Object *object: qAsConst(doc.objects)) { for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; const QString propName = doc.stringAt(binding->propertyNameIndex); if (!propName.startsWith(QLatin1String("on")) -- cgit v1.2.3 From 50dfcb2a16ef7b9c52a3beaa92a36569ffbcd85d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 3 May 2022 14:55:18 +0200 Subject: QML: Port QV4::CompiledData::Lookup to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I8cc6db56642f1cd2d16e80ba5c49ffd7c6fdcd8c Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 6d92633f32ff2089b8f0a39e07f0d40bf57d8011) --- src/qml/common/qv4compileddata_p.h | 19 +++++++++++++------ src/qml/compiler/qv4compiler.cpp | 20 ++++---------------- src/qml/jsruntime/qv4executablecompilationunit.cpp | 4 ++-- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 93e4c66493..1b137a0826 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -181,13 +181,20 @@ struct Lookup Type_QmlContextPropertyGetter = 3 }; - union { - quint32 _dummy; - quint32_le_bitfield<0, 4> type_and_flags; - quint32_le_bitfield<4, 28> nameIndex; - }; + quint32 typeAndFlags() const { return m_data.get(); } + quint32 nameIndex() const { return m_data.get(); } - Lookup() : _dummy(0) { } + Lookup() : m_data(QSpecialIntegerBitfieldZero) {} + Lookup(Type type, quint32 nameIndex) + { + m_data.set(type); + m_data.set(nameIndex); + } + +private: + using TypeAndFlagsField = quint32_le_bitfield_member<0, 4>; + using NameIndexField = quint32_le_bitfield_member<4, 28>; + quint32_le_bitfield_union m_data; }; static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 0e15f12053..c000861316 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -160,10 +160,7 @@ int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Getter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Getter, nameIndex); return lookups.size() - 1; } @@ -174,28 +171,19 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Setter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Setter, nameIndex); return lookups.size() - 1; } int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup(CompiledData::Lookup::Type_GlobalGetter, nameIndex); return lookups.size() - 1; } int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup(CompiledData::Lookup::Type_QmlContextPropertyGetter, nameIndex); return lookups.size() - 1; } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 972d0c0ce2..cff605b92f 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -169,7 +169,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) QV4::Lookup *l = runtimeLookups + i; CompiledData::Lookup::Type type - = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags)); + = CompiledData::Lookup::Type(uint(compiledLookups[i].typeAndFlags())); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -178,7 +178,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) l->globalGetter = QV4::Lookup::globalGetterGeneric; else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - l->nameIndex = compiledLookups[i].nameIndex; + l->nameIndex = compiledLookups[i].nameIndex(); } } -- cgit v1.2.3 From eb2257e8443777adbd455dc54ce507a10f832618 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 5 May 2022 13:57:26 +0200 Subject: QML: Port icutils::Node to new special integer bitfield Change-Id: I46f4f21bda1360d09e2c49a1f04dbe411fb46f7d Task-number: QTBUG-99545 Reviewed-by: Fabian Kosmale (cherry picked from commit eca5dcab020a60a53c0ad1b130cf2873d3feff3e) --- src/qml/inlinecomponentutils_p.h | 49 +++++++++++++--------- src/qml/jsruntime/qv4executablecompilationunit.cpp | 2 +- src/qml/qml/qqmlpropertycachecreator_p.h | 2 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h index eade87746e..b00d6b96cc 100644 --- a/src/qml/inlinecomponentutils_p.h +++ b/src/qml/inlinecomponentutils_p.h @@ -55,26 +55,38 @@ namespace icutils { struct Node { +private: + using IndexType = std::vector::size_type; + using IndexField = quint32_le_bitfield_member<0, 30, IndexType>; + using TemporaryMarkField = quint32_le_bitfield_member<30, 1>; + using PermanentMarkField = quint32_le_bitfield_member<31, 1>; + quint32_le_bitfield_union m_data; + +public: Node() = default; Node(const Node &) = default; Node(Node &&) = default; Node& operator=(Node const &) = default; Node& operator=(Node &&) = default; - bool operator==(Node const &other) const {return index == other.index;} + bool operator==(Node const &other) const {return m_data.data() == other.m_data.data(); } + + Node(IndexType s) : m_data(QSpecialIntegerBitfieldZero) { m_data.set(s); } + + bool hasPermanentMark() const { return m_data.get(); } + bool hasTemporaryMark() const { return m_data.get(); } + + void setPermanentMark() + { + m_data.set(0); + m_data.set(1); + } - Node(std::vector::size_type s) - : index{0} + void setTemporaryMark() { - index = quint32(s); - temporaryMark = 0; - permanentMark = 0; + m_data.set(1); } - union { - quint32_le_bitfield<0, 30> index; - quint32_le_bitfield<30, 1> temporaryMark; - quint32_le_bitfield<31, 1> permanentMark; - }; + IndexType index() const { return m_data.get(); } }; using AdjacencyList = std::vector>; @@ -120,21 +132,20 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja }; inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle, std::vector &nodesSorted) { - if (node->permanentMark) + if (node->hasPermanentMark()) return; - if (node->temporaryMark) { + if (node->hasTemporaryMark()) { hasCycle = true; return; } - node->temporaryMark = 1; + node->setTemporaryMark(); - auto const &edges = adjacencyList[node->index]; + auto const &edges = adjacencyList[node->index()]; for (auto edgeTarget =edges.begin(); edgeTarget != edges.end(); ++edgeTarget) { topoVisit(*edgeTarget, adjacencyList, hasCycle, nodesSorted); } - node->temporaryMark = 0; - node->permanentMark = 1; + node->setPermanentMark(); nodesSorted.push_back(*node); }; @@ -145,7 +156,7 @@ inline std::vector topoSort(std::vector &nodes, AdjacencyList &adjac hasCycle = false; auto currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) { - return node.permanentMark == 0; + return !node.hasPermanentMark(); }); // Do a topological sort of all inline components // afterwards, nodesSorted contains the nodes for the inline components in reverse topological order @@ -153,7 +164,7 @@ inline std::vector topoSort(std::vector &nodes, AdjacencyList &adjac Node& currentNode = *currentNodeIt; topoVisit(¤tNode, adjacencyList, hasCycle, nodesSorted); currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) { - return node.permanentMark == 0; + return !node.hasPermanentMark(); }); } return nodesSorted; diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index cff605b92f..b6c6138da5 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -421,7 +421,7 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi // We need to first iterate over all inline components, as the containing component might create instances of them // and in that case we need to add its object count for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { - const auto &ic = allICs.at(nodeIt->index); + const auto &ic = allICs.at(nodeIt->index()); int lastICRoot = ic.objectIndex; for (int i = ic.objectIndex; i::buildMetaObjects() // create meta objects for inline components before compiling actual root component for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { - const auto &ic = allICs[nodeIt->index]; + const auto &ic = allICs[nodeIt->index()]; QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex); Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr); Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet -- cgit v1.2.3 From 8ca617a178c316124e4c50f79dded853e889f140 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 5 May 2022 12:17:11 +0200 Subject: QML: Port QV4::CompiledData::Object to new special integer bitfield Task-number: QTBUG-99545 Change-Id: Ia57a16313e883a8d4dab15c971181440ed1d2214 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit 745cce4391a8b6255605cb304d8bc14b11168423) --- src/qml/common/qv4compileddata_p.h | 57 +++++++++++++++++++--- src/qml/compiler/qqmlirbuilder.cpp | 6 +-- src/qml/compiler/qqmlirbuilder_p.h | 4 ++ src/qml/inlinecomponentutils_p.h | 7 ++- src/qml/jsruntime/qv4executablecompilationunit.cpp | 13 ++--- src/qml/qml/qqmlirloader.cpp | 9 ++-- src/qml/qml/qqmlobjectcreator.cpp | 12 ++--- src/qml/qml/qqmlpropertycachecreator_p.h | 12 ++--- src/qml/qml/qqmlpropertyvalidator.cpp | 3 +- src/qmltest/quicktest.cpp | 2 +- 10 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 1b137a0826..f818e45d11 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -872,7 +872,12 @@ static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected s struct Object { - enum Flags : unsigned int { +private: + using FlagsField = quint32_le_bitfield_member<0, 15>; + using DefaultPropertyIsAliasField = quint32_le_bitfield_member<15, 1>; + using IdField = quint32_le_bitfield_member<16, 16, qint32>; +public: + enum Flag : unsigned int { NoFlag = 0x0, IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary HasDeferredBindings = 0x2, // any of the bindings are deferred @@ -880,17 +885,15 @@ struct Object IsInlineComponentRoot = 0x8, InPartOfInlineComponent = 0x10 }; + Q_DECLARE_FLAGS(Flags, Flag); // Depending on the use, this may be the type name to instantiate before instantiating this // object. For grouped properties the type name will be empty and for attached properties // it will be the name of the attached type. quint32_le inheritedTypeNameIndex; quint32_le idNameIndex; - union { - quint32_le_bitfield<0, 15> flags; - quint32_le_bitfield<15, 1> defaultPropertyIsAlias; - qint32_le_bitfield<16, 16> id; - }; + quint32_le_bitfield_union + flagsAndDefaultPropertyIsAliasAndId; qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object quint16_le nFunctions; quint16_le nProperties; @@ -919,6 +922,48 @@ struct Object // InlineComponent[] // RequiredPropertyExtraData[] + Flags flags() const + { + return Flags(flagsAndDefaultPropertyIsAliasAndId.get()); + } + + bool hasFlag(Flag flag) const + { + return flagsAndDefaultPropertyIsAliasAndId.get() & flag; + } + + void setFlag(Flag flag) + { + flagsAndDefaultPropertyIsAliasAndId.set( + flagsAndDefaultPropertyIsAliasAndId.get() | flag); + } + + void setFlags(Flags flags) + { + flagsAndDefaultPropertyIsAliasAndId.set(flags); + } + + bool hasAliasAsDefaultProperty() const + { + return flagsAndDefaultPropertyIsAliasAndId.get(); + } + + void setHasAliasAsDefaultProperty(bool defaultAlias) + { + flagsAndDefaultPropertyIsAliasAndId.set(defaultAlias); + } + + qint32 objectId() const + { + return flagsAndDefaultPropertyIsAliasAndId.get(); + } + + void setObjectId(qint32 id) + { + flagsAndDefaultPropertyIsAliasAndId.set(id); + } + + static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData) { return ( sizeof(Object) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 15b333584e..bc4b537a6f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1672,10 +1672,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen QV4::CompiledData::Object *objectToWrite = reinterpret_cast(objectPtr); objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias; - objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias; - objectToWrite->flags = o->flags; + objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias); + objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags)); objectToWrite->idNameIndex = o->idNameIndex; - objectToWrite->id = o->id; + objectToWrite->setObjectId(o->id); objectToWrite->location = o->location; objectToWrite->locationOfIdProperty = o->locationOfIdProperty; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 665c90215d..7cf6492471 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -394,6 +394,10 @@ public: int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); } const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } + bool hasFlag(QV4::CompiledData::Object::Flag flag) const { return flags & flag; } + qint32 objectId() const { return id; } + bool hasAliasAsDefaultProperty() const { return defaultPropertyIsAlias; } + private: friend struct ::QQmlIRLoader; diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h index b00d6b96cc..aba15709c2 100644 --- a/src/qml/inlinecomponentutils_p.h +++ b/src/qml/inlinecomponentutils_p.h @@ -121,8 +121,11 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja auto referencedInICObjectIndex = ic.objectIndex + 1; while (int(referencedInICObjectIndex) < objectContainer->objectCount()) { auto potentiallyReferencedInICObject = objectContainer->objectAt(referencedInICObjectIndex); - bool stillInIC = !(potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::IsInlineComponentRoot) - && (potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::InPartOfInlineComponent); + bool stillInIC + = !potentiallyReferencedInICObject->hasFlag( + QV4::CompiledData::Object::IsInlineComponentRoot) + && potentiallyReferencedInICObject->hasFlag( + QV4::CompiledData::Object::InPartOfInlineComponent); if (!stillInIC) break; createEdgeFromTypeRef(objectContainer->resolvedType(potentiallyReferencedInICObject->inheritedTypeNameIndex)); diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index b6c6138da5..5cf496255f 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -368,7 +368,7 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr); - namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId()); } return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } @@ -425,9 +425,10 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi int lastICRoot = ic.objectIndex; for (int i = ic.objectIndex; iflags & QV4::CompiledData::Object::IsInlineComponentRoot) - || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent); + bool leftCurrentInlineComponent + = (i != lastICRoot + && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot)) + || !obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent); if (leftCurrentInlineComponent) break; inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings; @@ -457,9 +458,9 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi int objectCount = 0; for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { const QV4::CompiledData::Object *obj = objectAt(i); - if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) { + if (obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent)) continue; - } + bindingCount += obj->nBindings; if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1) diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp index 7805f014f8..3c82ffd146 100644 --- a/src/qml/qml/qqmlirloader.cpp +++ b/src/qml/qml/qqmlirloader.cpp @@ -94,10 +94,11 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->isInlineComponent = serializedObject->flags & QV4::CompiledData::Object::IsInlineComponentRoot; - object->flags = serializedObject->flags; - object->id = serializedObject->id; + object->defaultPropertyIsAlias = serializedObject->hasAliasAsDefaultProperty(); + object->isInlineComponent = serializedObject->hasFlag( + QV4::CompiledData::Object::IsInlineComponentRoot); + object->flags = serializedObject->flags(); + object->id = serializedObject->objectId(); object->location = serializedObject->location; object->locationOfIdProperty = serializedObject->locationOfIdProperty; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 0d6fbe6f36..3b875d5253 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1155,8 +1155,8 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const { - if (object->id >= 0) - context->setIdProperty(object->id, instance); + if (object->objectId() >= 0) + context->setIdProperty(object->objectId(), instance); } void QQmlObjectCreator::createQmlContext() @@ -1181,7 +1181,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QQmlParserStatus *parserStatus = nullptr; bool installPropertyCache = true; - if (obj->flags & QV4::CompiledData::Object::IsComponent) { + if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)) { isComponent = true; QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent); typeName = QStringLiteral(""); @@ -1279,7 +1279,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo ddata->setImplicitDestructible(); // inline components are root objects, but their index is != 0, so we need // an additional check - const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot; + const bool isInlineComponent = obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot); if (static_cast(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) { if (ddata->context) { Q_ASSERT(ddata->context != context); @@ -1312,7 +1312,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (isContextObject) context->contextObject = instance; - if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { + if (customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) { customParser->engine = QQmlEnginePrivate::get(engine); customParser->imports = compilationUnit->typeNameCache.data(); @@ -1517,7 +1517,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * qSwap(_propertyCache, cache); qSwap(_vmeMetaObject, vmeMetaObject); - if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) + if (_compiledObject->hasFlag(QV4::CompiledData::Object::HasDeferredBindings)) _ddata->deferData(_compiledObjectIndex, compilationUnit, context); QSet postHocRequired; diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 8ebbe07e37..ebee50cc79 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -226,7 +226,7 @@ inline QQmlError QQmlPropertyCacheCreator::buildMetaObjectRecur const CompiledObject *obj = objectContainer->objectAt(objectIndex); bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 - || (((obj->flags & QV4::CompiledData::Object::IsComponent) + || ((obj->hasFlag(QV4::CompiledData::Object::IsComponent) || (objectIndex == 0 && isAddressable(objectContainer->url()))) && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType); @@ -618,7 +618,7 @@ inline QQmlError QQmlPropertyCacheCreator::createMetaObject(int propertyFlags.setIsWritable(true); QString propertyName = stringAt(p->nameIndex); - if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + if (!obj->hasAliasAsDefaultProperty() && propertyIdx == obj->indexOfDefaultPropertyOrAlias) cache->_defaultPropertyName = propertyName; cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, propertyType, propertTypeMinorVersion, effectiveSignalIndex); @@ -704,7 +704,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie // from a binding. for (int i = 1; i < objectContainer->objectCount(); ++i) { const CompiledObject &component = *objectContainer->objectAt(i); - if (!(component.flags & QV4::CompiledData::Object::IsComponent)) + if (!component.hasFlag(QV4::CompiledData::Object::IsComponent)) continue; const auto rootBinding = component.bindingsBegin(); @@ -774,7 +774,7 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl objectsWithAliases->append(objectIndex); // Stop at Component boundary - if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) + if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0) return; auto binding = object.bindingsBegin(); @@ -941,7 +941,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator::appendAliasesTo const QString propertyName = objectContainer->stringAt(alias->nameIndex()); - if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) + if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias) propertyCache->_defaultPropertyName = propertyName; propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, @@ -957,7 +957,7 @@ inline int QQmlPropertyCacheAliasCreator::objectForId(const Com for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { const int candidateIndex = component.namedObjectsInComponentTable()[i]; const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); - if (candidate.id == id) + if (candidate.objectId() == id) return candidateIndex; } return -1; diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 8bb95045a4..f3947a73d4 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -104,7 +104,8 @@ QVector QQmlPropertyValidator::validateObject( validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr); } - if (obj->flags & QV4::CompiledData::Object::IsComponent && !(obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)) { + if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) + && !obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot)) { Q_ASSERT(obj->nBindings == 1); const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object); diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 40f957ae0d..6f168989c0 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -309,7 +309,7 @@ private: if (!object) // Start at root of compilation unit if not enumerating a specific child object = compilationUnit->objectAt(0); - if (object->flags & Object::IsInlineComponentRoot) + if (object->hasFlag(Object::IsInlineComponentRoot)) return result; if (const auto superTypeUnit = compilationUnit->resolvedTypes.value( -- cgit v1.2.3 From 9169558da85410168c792b2311089d1c4d20d123 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 4 May 2022 11:32:51 +0200 Subject: QML: Port QV4::CompiledData::JSClassMember to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I0a7d86450011f1664d61db4d78317dafbcfbb8cf Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit b5a8a6943ab979e23db284780df9209af5ae03a8) Reviewed-by: Qt Cherry-pick Bot --- src/qml/common/qv4compileddata_p.h | 20 ++++++++++++++------ src/qml/compiler/qv4compiler.cpp | 3 +-- src/qml/jsruntime/qv4executablecompilationunit.cpp | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index f818e45d11..43137eab3b 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -200,13 +200,21 @@ static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected struct JSClassMember { - union { - quint32 _dummy; - quint32_le_bitfield<0, 31> nameOffset; - quint32_le_bitfield<31, 1> isAccessor; - }; + JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {} + + void set(quint32 nameOffset, bool isAccessor) + { + m_data.set(nameOffset); + m_data.set(isAccessor ? 1 : 0); + } - JSClassMember() : _dummy(0) { } + quint32 nameOffset() const { return m_data.get(); } + bool isAccessor() const { return m_data.get() != 0; } + +private: + using NameOffsetField = quint32_le_bitfield_member<0, 31>; + using IsAccessorField = quint32_le_bitfield_member<31, 1>; + quint32_le_bitfield_union m_data; }; static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c000861316..c3c5e69995 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -237,8 +237,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members) CompiledData::JSClassMember *member = reinterpret_cast(jsClass + 1); for (const auto &name : members) { - member->nameOffset = registerString(name); - member->isAccessor = false; + member->set(registerString(name), false); ++member; } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 5cf496255f..f32fde19b8 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -199,8 +199,8 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeClasses[i] = runtimeClasses[i]->addMember( engine->identifierTable->asPropertyKey( - runtimeStrings[member->nameOffset]), - member->isAccessor + runtimeStrings[member->nameOffset()]), + member->isAccessor() ? QV4::Attr_Accessor : QV4::Attr_Data); } -- cgit v1.2.3 From f9022e847fd8717622a06ef042449be6436ce24a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 3 May 2022 14:54:22 +0200 Subject: QML: Port QV4::CompiledData::RegExp to new special integer bitfield Task-number: QTBUG-99545 Change-Id: I37be080387bf086d84761b056140cc5a99d161ed Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit da09f7c3d8962b4521189c96adf1ed0e1da3e8dd) Reviewed-by: Qt Cherry-pick Bot --- src/qml/common/qv4compileddata_p.h | 20 ++++++++++++++------ src/qml/compiler/qv4compiler.cpp | 17 +++++++---------- src/qml/jsruntime/qv4executablecompilationunit.cpp | 4 ++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 43137eab3b..baf2d3fac8 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -162,13 +162,21 @@ struct RegExp RegExp_Unicode = 0x08, RegExp_Sticky = 0x10 }; - union { - quint32 _dummy; - quint32_le_bitfield<0, 5> flags; - quint32_le_bitfield<5, 27> stringIndex; - }; - RegExp() : _dummy(0) { } + RegExp() : m_data(QSpecialIntegerBitfieldZero) {} + RegExp(quint32 flags, quint32 stringIndex) + { + m_data.set(flags); + m_data.set(stringIndex); + } + + quint32 flags() const { return m_data.get(); } + quint32 stringIndex() const { return m_data.get(); } + +private: + using FlagsField = quint32_le_bitfield_member<0, 5>; + using StringIndexField = quint32_le_bitfield_member<5, 27>; + quint32_le_bitfield_union m_data; }; static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c3c5e69995..5e17c82dae 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -189,22 +189,19 @@ int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int n int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp) { - CompiledData::RegExp re; - re.stringIndex = registerString(regexp->pattern.toString()); - - re.flags = 0; + quint32 flags = 0; if (regexp->flags & QQmlJS::Lexer::RegExp_Global) - re.flags |= CompiledData::RegExp::RegExp_Global; + flags |= CompiledData::RegExp::RegExp_Global; if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase) - re.flags |= CompiledData::RegExp::RegExp_IgnoreCase; + flags |= CompiledData::RegExp::RegExp_IgnoreCase; if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline) - re.flags |= CompiledData::RegExp::RegExp_Multiline; + flags |= CompiledData::RegExp::RegExp_Multiline; if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode) - re.flags |= CompiledData::RegExp::RegExp_Unicode; + flags |= CompiledData::RegExp::RegExp_Unicode; if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky) - re.flags |= CompiledData::RegExp::RegExp_Sticky; + flags |= CompiledData::RegExp::RegExp_Sticky; - regexps.append(re); + regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString()))); return regexps.size() - 1; } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index f32fde19b8..50334f85a7 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -155,10 +155,10 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); - uint f = re->flags; + uint f = re->flags(); const CompiledData::RegExp::Flags flags = static_cast(f); runtimeRegularExpressions[i] = QV4::RegExp::create( - engine, stringAt(re->stringIndex), flags); + engine, stringAt(re->stringIndex()), flags); } if (data->lookupTableSize) { -- cgit v1.2.3 From 2cc85e60d4313bd845bfdc233e5e75a8b7ccb01f Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Wed, 22 Jun 2022 19:43:29 +0200 Subject: Add listing of the components and scripts that belongs to the qml module Add qml components and scripts to the qmlimportscanner output to give information about files that actually belong to the qml module. Also remove dependency duplicates to reduce the amount of scans are made by qmlimporscanner. Task-number: QTBUG-97834 Task-number: QTBUG-36637 Change-Id: Ic2533a827a716cd9c4e3a2be54ac0beafa666aaa (cherry picked from commit 1d135de5cfef6da7457e5caf1612c0c112cfea7b) Reviewed-by: Alexandru Croitor --- tools/qmlimportscanner/main.cpp | 66 +++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 9f6a89810f..52eebcdf65 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -71,6 +71,14 @@ inline QString dependenciesLiteral() { return QStringLiteral("dependencies"); } inline QString moduleLiteral() { return QStringLiteral("module"); } inline QString javascriptLiteral() { return QStringLiteral("javascript"); } inline QString directoryLiteral() { return QStringLiteral("directory"); } +inline QString componentsLiteral() +{ + return QStringLiteral("components"); +} +inline QString scriptsLiteral() +{ + return QStringLiteral("scripts"); +} void printUsage(const QString &appNameIn) { @@ -150,21 +158,42 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { QString plugins; QString classnames; QStringList dependencies; + QStringList componentFiles; + QStringList scriptFiles; QByteArray line; do { line = qmldirFile.readLine(); - if (line.startsWith("plugin")) { - plugins += QString::fromUtf8(line.split(' ').at(1)); - plugins += QLatin1Char(' '); - } else if (line.startsWith("classname")) { - classnames += QString::fromUtf8(line.split(' ').at(1)); - classnames += QLatin1Char(' '); - } else if (line.startsWith("depends")) { - const QList dep = line.split(' '); - if (dep.length() != 3) - std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl; - else - dependencies << QString::fromUtf8(dep[1]) + QLatin1Char(' ') + QString::fromUtf8(dep[2]).simplified(); + const QList sections = line.split(' '); + if (sections.size() > 0) { + const QByteArray §ionType = sections.at(0); + if (sectionType == "plugin") { + plugins += QString::fromUtf8(line.split(' ').at(1)); + plugins += QLatin1Char(' '); + } else if (sectionType == "classname") { + classnames += QString::fromUtf8(line.split(' ').at(1)); + classnames += QLatin1Char(' '); + } else if (sectionType == "depends") { + if (sections.size() != 3) + std::cerr << "depends: expected 2 arguments: module identifier and version" + << std::endl; + else + dependencies << QString::fromUtf8(sections.at(1)) + QLatin1Char(' ') + + QString::fromUtf8(sections.at(2)).simplified(); + } else if (sections.size() == 2 && sectionType != "module" + && sectionType != "typeinfo") { + componentFiles.append(modulePath + QLatin1Char('/') + + QString::fromUtf8(sections.at(1))); + } else if (sections.size() == 3 + || (sectionType == "singleton" && sections.size() == 4)) { + int fileNameIndex = (sectionType == "singleton") ? 3 : 2; + const QString fileName = QString::fromUtf8(sections.at(fileNameIndex)); + const QString filePath = modulePath + QLatin1Char('/') + fileName; + if (fileName.endsWith(QLatin1String(".js")) + || fileName.endsWith(QLatin1String(".mjs"))) + scriptFiles.append(filePath); + else + componentFiles.append(filePath); + } } } while (line.length() > 0); @@ -173,7 +202,12 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { pluginInfo[pluginsLiteral()] = plugins.simplified(); pluginInfo[classnamesLiteral()] = classnames.simplified(); if (dependencies.length()) + dependencies.removeDuplicates(); pluginInfo[dependenciesLiteral()] = dependencies; + if (!componentFiles.isEmpty()) + pluginInfo[componentsLiteral()] = componentFiles; + if (!scriptFiles.isEmpty()) + pluginInfo[scriptsLiteral()] = scriptFiles; return pluginInfo; } @@ -247,6 +281,8 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) QVariantMap plugininfo = pluginsForModulePath(import.value(pathLiteral()).toString()); QString plugins = plugininfo.value(pluginsLiteral()).toString(); QString classnames = plugininfo.value(classnamesLiteral()).toString(); + QStringList components = plugininfo.value(componentsLiteral()).toStringList(); + QStringList scripts = plugininfo.value(scriptsLiteral()).toStringList(); if (!plugins.isEmpty()) import.insert(QStringLiteral("plugin"), plugins); if (!classnames.isEmpty()) @@ -262,6 +298,12 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) importsCopy.append(depImport); } } + if (!components.isEmpty()) { + import.insert(componentsLiteral(), components); + } + if (!scripts.isEmpty()) { + import.insert(scriptsLiteral(), scripts); + } } done.append(import); } -- cgit v1.2.3 From 63d78523a98b40283b400445bae6f3371eed181b Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 6 Jul 2022 10:05:49 +0200 Subject: masm: fix -Wdeprecated-enum-enum-conversion C++20 deprecated mixed-enum arithmetic. Cast one of the enums to int to suppress the warning. Fixes: QTBUG-103943 Change-Id: I157be3368d6a0f201f3f4ddd7248479ee544d32d Reviewed-by: Ulf Hermann (cherry picked from commit c4bddc4765c9c21da1e69ecc3ed4b7897bf14e41) Reviewed-by: Qt Cherry-pick Bot --- src/3rdparty/masm/assembler/X86Assembler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h index e8ae687036..8f9ee29a4d 100644 --- a/src/3rdparty/masm/assembler/X86Assembler.h +++ b/src/3rdparty/masm/assembler/X86Assembler.h @@ -201,12 +201,12 @@ private: TwoByteOpcodeID jccRel32(Condition cond) { - return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); + return (TwoByteOpcodeID)(int(OP2_JCC_rel32) + cond); } TwoByteOpcodeID setccOpcode(Condition cond) { - return (TwoByteOpcodeID)(OP_SETCC + cond); + return (TwoByteOpcodeID)(int(OP_SETCC) + cond); } typedef enum { -- cgit v1.2.3 From 0ff8be24fce1051fdefea269a64d5eceddc9d371 Mon Sep 17 00:00:00 2001 From: Samuel Mira Date: Fri, 15 Jul 2022 14:04:07 +0300 Subject: Android: Fix crash on tap handler with a S-Pen This patch fixes a crash using a S-Pen with QML TapHandler on Qt 5.15. The reason it was happening was because the item->window() was nullptr when it tried to run the mapToGlobal function. The item->windows becomes nullptr on handlePointerEvent function, so switching both lines is enough to fix this crash. Fixes: QTBUG-86805 Change-Id: I25f20650e97b5ad094a99c810379fb412fdb4222 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 18bf8212d1..e48b83d5fc 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2209,6 +2209,8 @@ bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEven itemPrivate->handlePointerEvent(event); if (point->isAccepted()) return true; + if (!item->window()) + continue; QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint()); #if QT_CONFIG(wheelevent) // Let the Item have a chance to handle it -- cgit v1.2.3 From 9cdf70efb561cb5ae578ce44f44eb4c8fcb627c2 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 18 Mar 2022 07:21:47 +0100 Subject: QQuickText/Edit: fix C++20 -Wdeprecated-enum-enum-conversion warnings Declare that HAlignment and VAlignment are designed to be mixed. Change-Id: I97663653014856a3391e36dd1847b67603f6aac9 Reviewed-by: Fabian Kosmale (cherry picked from commit c34cdee53f81ad0d645468ebae7aa9d8cab79a3f) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktext_p.h | 2 ++ src/quick/items/qquicktextedit_p.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index 0b56704b86..84c5cbb45b 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -326,6 +326,8 @@ private: Q_DECLARE_PRIVATE(QQuickText) }; +Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickText::HAlignment, QQuickText::VAlignment) + class QTextLine; class QQuickTextLine : public QObject { diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 871122d4cf..d79b876903 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -422,6 +422,8 @@ private: Q_DECLARE_PRIVATE(QQuickTextEdit) }; +Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickTextEdit::HAlignment, QQuickTextEdit::VAlignment) + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickTextEdit) -- cgit v1.2.3 From ca50ca83f7f4aedcf5d85b9532d12a9401cf67cf Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 1 Jun 2022 11:12:12 +0200 Subject: QV4::CompiledData: fix GCC 12 -Werror=uninitialized errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The non-default ctors tried to call m_data.set(), where m_data uninitialized. Says GCC 12: In member function ‘QSpecialIntegerAccessor& QSpecialIntegerAccessor::operator=(Type) [with S = QLittleEndianStorageType; int pos = 0; int width = 5; T = unsigned int]’, inlined from ‘void QSpecialIntegerBitfieldUnion::set(typename A::Type) [with A = QSpecialIntegerAccessor, 0, 5, unsigned int>; S = QLittleEndianStorageType; Accessors = {QSpecialIntegerAccessor, 0, 5, unsigned int>, QSpecialIntegerAccessor, 5, 27, unsigned int>}]’ at qtbase/src/corelib/global/qendian_p.h:214:21, inlined from ‘QV4::CompiledData::RegExp::RegExp(quint32, quint32)’ at qtdeclarative/src/qml/common/qv4compileddata_p.h:187:31, inlined from ‘int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral*)’ at qtdeclarative/src/qml/compiler/qv4compiler.cpp:198:34: qtbase/src/corelib/global/qendian_p.h:179:40: error: ‘.QV4::CompiledData::RegExp::m_data.QSpecialIntegerBitfieldUnion, QSpecialIntegerAccessor, 0, 5, unsigned int>, QSpecialIntegerAccessor, 5, 27, unsigned int> >::storage.QSpecialIntegerStorage >::val’ is used uninitialized [-Werror=uninitialized] 179 | UnsignedType i = S::fromSpecial(storage->val); | ~~~~~~~~~~~~~~^~~~~~~~~~~~~~ qtdeclarative/src/qml/compiler/qv4compiler.cpp: In member function ‘int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral*)’: qtdeclarative/src/qml/compiler/qv4compiler.cpp:198:90: note: ‘’ declared here 198 | regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString()))); | ^ Fix by calling the default ctor (which initialized m_data) before calling m_data.set(). Task-number: QTBUG-103924 Change-Id: I44ff404e5509e24601893e539639f213defdc80d Reviewed-by: Fabian Kosmale Reviewed-by: Ulf Hermann (cherry picked from commit 405f1f7d05a27f659fa47afb5574a159301caff6) --- src/qml/common/qv4compileddata_p.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index baf2d3fac8..be7b61653c 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -121,7 +121,7 @@ struct TableIterator struct Location { Location() : m_data(QSpecialIntegerBitfieldZero) {} - Location(quint32 l, quint32 c) + Location(quint32 l, quint32 c) : Location() { m_data.set(l); m_data.set(c); @@ -164,7 +164,7 @@ struct RegExp }; RegExp() : m_data(QSpecialIntegerBitfieldZero) {} - RegExp(quint32 flags, quint32 stringIndex) + RegExp(quint32 flags, quint32 stringIndex) : RegExp() { m_data.set(flags); m_data.set(stringIndex); @@ -193,7 +193,7 @@ struct Lookup quint32 nameIndex() const { return m_data.get(); } Lookup() : m_data(QSpecialIntegerBitfieldZero) {} - Lookup(Type type, quint32 nameIndex) + Lookup(Type type, quint32 nameIndex) : Lookup() { m_data.set(type); m_data.set(nameIndex); -- cgit v1.2.3 From dc69a005755c4d4c1b3de6cd52321b543b49a1a1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 2 Aug 2022 15:47:58 +0200 Subject: Qml: Don't crash on nested group properties with aliases This fixes an oversight where in case of half-resolved grouped properties we would forget to add the bindings to the "pending" list. In addition we would fail to resolve their property caches later on. Fixes: QTBUG-94983 Change-Id: I88bd0ce56464438d2a105e5ed426e002495cc016 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale (cherry picked from commit fed5b07980d9ae5a392a00563f70ee34fc261dbc) --- src/qml/qml/qqmlpropertycachecreator.cpp | 5 +++ src/qml/qml/qqmlpropertycachecreator_p.h | 36 +++++++++++----------- tests/auto/qml/qqmllanguage/data/alias.15a.qml | 12 ++++++++ .../auto/qml/qqmllanguage/data/fuzzed.1.errors.txt | 3 +- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 16 ++++++++++ 5 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/alias.15a.qml diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index 65befa073d..bb601cea9d 100644 --- a/src/qml/qml/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -146,6 +146,11 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePr if (propertyCaches->at(groupPropertyObjectIndex)) continue; + if (!pendingBinding.referencingObjectPropertyCache) { + pendingBinding.referencingObjectPropertyCache + = propertyCaches->at(pendingBinding.referencingObjectIndex); + } + if (!pendingBinding.resolveInstantiatingProperty()) continue; diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index ebee50cc79..8d28ad009d 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -277,24 +277,24 @@ inline QQmlError QQmlPropertyCacheCreator::buildMetaObjectRecur } } - if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { - auto binding = obj->bindingsBegin(); - auto end = obj->bindingsEnd(); - for ( ; binding != end; ++binding) - if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { - QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); - - // Binding to group property where we failed to look up the type of the - // property? Possibly a group property that is an alias that's not resolved yet. - // Let's attempt to resolve it after we're done with the aliases and fill in the - // propertyCaches entry then. - if (!context.resolveInstantiatingProperty()) - pendingGroupPropertyBindings->append(context); - - QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe); - if (error.isValid()) - return error; - } + QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex); + auto binding = obj->bindingsBegin(); + auto end = obj->bindingsEnd(); + for ( ; binding != end; ++binding) { + if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { + QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + + // Binding to group property where we failed to look up the type of the + // property? Possibly a group property that is an alias that's not resolved yet. + // Let's attempt to resolve it after we're done with the aliases and fill in the + // propertyCaches entry then. + if (!thisCache || !context.resolveInstantiatingProperty()) + pendingGroupPropertyBindings->append(context); + + QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe); + if (error.isValid()) + return error; + } } QQmlError noError; diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml new file mode 100644 index 0000000000..ba8097c997 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 + +Item { + id: root + + property alias symbol: symbol + symbol.layer.enabled: true + + Item { + id: symbol + } +} diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt index e399799fe9..758be7feae 100644 --- a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt @@ -1,2 +1 @@ -2:8:Cannot assign to non-existent property "_G" - +2:11:Non-existent attached object diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index bffb62c59e..48cf20cf38 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2146,6 +2146,22 @@ void tst_qqmllanguage::aliasProperties() QCOMPARE(subItem->property("y").toInt(), 1); } + // Nested property bindings on group properties that are actually aliases (QTBUG-94983) + { + QQmlComponent component(&engine, testFileUrl("alias.15a.qml")); + VERIFY_ERRORS(0); + + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QPointer subItem = qvariant_cast(object->property("symbol")); + QVERIFY(!subItem.isNull()); + + QPointer subSubItem = qvariant_cast(subItem->property("layer")); + + QCOMPARE(subSubItem->property("enabled").value(), true); + } + // Alias to sub-object with binding (QTBUG-57041) { // This is shold *not* crash. -- cgit v1.2.3 From 6af6258de22410ecb33a010a8b92d70a4e2a6f08 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 Aug 2022 09:43:51 +0200 Subject: doc: Add missing PointerHandler.CanTakeOverFromItems enum value Amends 0a3eec60cab3c453b378ee45ac335e0dc2951f4b Change-Id: Iae9d1b2b68dc48a52adf0438a09af8e53f5527f1 Reviewed-by: Richard Moe Gustavsen (cherry picked from commit c2d92c241274f9abdcb24637f9838210f191e8ed) Reviewed-by: Qt Cherry-pick Bot --- src/quick/handlers/qquickpointerhandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 9e9c2e8d6f..f57a2e5aa3 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -421,6 +421,8 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec This handler can take the exclusive grab from another handler of the same class. \value PointerHandler.CanTakeOverFromHandlersOfDifferentType This handler can take the exclusive grab from any kind of handler. + \value PointerHandler.CanTakeOverFromItems + This handler can take the exclusive grab from any type of Item. \value PointerHandler.CanTakeOverFromAnything This handler can take the exclusive grab from any type of Item or Handler. \value PointerHandler.ApprovesTakeOverByHandlersOfSameType -- cgit v1.2.3 From 167559e6408c492db4ad6477e90af4de2db39408 Mon Sep 17 00:00:00 2001 From: Jens Trillmann Date: Mon, 27 Jun 2022 16:29:09 +0200 Subject: A11Y: Send Scrolling Events when Flickable moves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-103513 Change-Id: I6b67ff2611f37a6519420d875e7d9a70d0eb210a Reviewed-by: Shawn Rutledge Reviewed-by: Assam Boudjelthia Reviewed-by: Jan Arve Sæther (cherry picked from commit ac0d923de68cad62d87e116fbb5e5cc2af28349c) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickflickable.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index c64825166a..f4b2379979 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -2766,6 +2766,12 @@ void QQuickFlickable::movementStarting() if (!wasMoving && (d->hData.moving || d->vData.moving)) { emit movingChanged(); emit movementStarted(); +#if QT_CONFIG(accessibility) + if (QAccessible::isActive()) { + QAccessibleEvent ev(this, QAccessible::ScrollingStart); + QAccessible::updateAccessibility(&ev); + } +#endif } } @@ -2810,6 +2816,12 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding) if (wasMoving && !isMoving()) { emit movingChanged(); emit movementEnded(); +#if QT_CONFIG(accessibility) + if (QAccessible::isActive()) { + QAccessibleEvent ev(this, QAccessible::ScrollingEnd); + QAccessible::updateAccessibility(&ev); + } +#endif } if (hMovementEnding) { -- cgit v1.2.3 From e404db276e958bcfd462c7e0cb2db89d11bedc44 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Jul 2022 13:50:10 +0200 Subject: V4: Mark InternalClass parents when running GC We need to preserve them as they notify us about protoId related changes. In order to avoid wasting heap space in case many properties are added and removed from the same object, we put a mechanism in place to rebuild the InternalClass hierarchy if many redundant transitions are detected. Amends commit 69d76d59cec0dcff4c52eef24e779fbef14beeca. Fixes: QTBUG-91687 Task-number: QTBUG-58559 Change-Id: I3238931b5919ed2b98059e0b7f928334284ce7bf Reviewed-by: Fabian Kosmale (cherry picked from commit 0b9fa18dfefc06f542bd0c98b7e41fa14aa0c2cf) --- src/qml/jsruntime/qv4engine.cpp | 2 +- src/qml/jsruntime/qv4global_p.h | 1 - src/qml/jsruntime/qv4internalclass.cpp | 170 ++++++++++++++++----- src/qml/jsruntime/qv4internalclass_p.h | 38 +++-- src/qml/jsruntime/qv4object.cpp | 59 +++++-- src/qml/jsruntime/qv4propertykey_p.h | 5 + .../qqmlecmascript/data/internalClassParentGc.js | 29 ++++ .../qqmlecmascript/data/internalClassParentGc.qml | 13 ++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 11 ++ tests/auto/qml/qv4mm/tst_qv4mm.cpp | 89 +++++++++-- 10 files changed, 341 insertions(+), 76 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js create mode 100644 tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f66de35428..828d755e0e 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -2030,7 +2030,7 @@ void ExecutionEngine::setQmlEngine(QQmlEngine *engine) static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) { - if (object->as() || object->internalClass()->isFrozen) + if (object->as() || object->internalClass()->isFrozen()) return; QV4::Scope scope(v4); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 7a7720020b..56369c1103 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -303,7 +303,6 @@ struct PropertyAttributes void clear() { m_all = 0; } bool isEmpty() const { return !m_all; } - uint flags() const { return m_flags; } uint all() const { return m_all; } bool operator==(PropertyAttributes other) { diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index fd98a293d6..c0d6657fce 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -178,7 +178,7 @@ void SharedInternalClassDataPrivate::setSize(uint s) data->values.size = s; } -PropertyKey SharedInternalClassDataPrivate::at(uint i) +PropertyKey SharedInternalClassDataPrivate::at(uint i) const { Q_ASSERT(data && i < size()); return PropertyKey::fromId(data->values.values[i].rawValue()); @@ -265,6 +265,13 @@ namespace Heap { void InternalClass::init(ExecutionEngine *engine) { +// InternalClass is automatically zeroed during allocation: +// prototype = nullptr; +// parent = nullptr; +// size = 0; +// numRedundantTransitions = 0; +// flags = 0; + Base::init(); new (&propertyTable) PropertyHash(); new (&nameMap) SharedInternalClassData(engine); @@ -273,13 +280,6 @@ void InternalClass::init(ExecutionEngine *engine) this->engine = engine; vtable = QV4::InternalClass::staticVTable(); -// prototype = nullptr; -// parent = nullptr; -// size = 0; - extensible = true; - isFrozen = false; - isSealed = false; - isUsedAsProto = false; protoId = engine->newProtoId(); // Also internal classes need an internal class pointer. Simply make it point to itself @@ -300,10 +300,8 @@ void InternalClass::init(Heap::InternalClass *other) prototype = other->prototype; parent = other; size = other->size; - extensible = other->extensible; - isSealed = other->isSealed; - isFrozen = other->isFrozen; - isUsedAsProto = other->isUsedAsProto; + numRedundantTransitions = other->numRedundantTransitions; + flags = other->flags; protoId = engine->newProtoId(); internalClass.set(engine, other->internalClass); @@ -365,7 +363,99 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) ++newClass->size; } -Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) +static PropertyAttributes attributesFromFlags(int flags) +{ + PropertyAttributes attributes; + attributes.m_all = uchar(flags); + return attributes; +} + +static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig) +{ + if (++orig->numRedundantTransitions < Heap::InternalClass::MaxRedundantTransitions) + return orig; + + // We will generally add quite a few transitions here. We have 255 redundant ones. + // We can expect at least as many significant ones in addition. + std::vector transitions; + + Scope scope(orig->engine); + Scoped child(scope, orig); + + { + quint8 remainingRedundantTransitions = orig->numRedundantTransitions; + QSet properties; + int structureChanges = 0; + + Scoped parent(scope, orig->parent); + while (parent && remainingRedundantTransitions > 0) { + Q_ASSERT(child->d() != scope.engine->classes[ExecutionEngine::Class_Empty]); + const auto it = std::find_if( + parent->d()->transitions.begin(), parent->d()->transitions.end(), + [&child](const InternalClassTransition &t) { + return child->d() == t.lookup; + }); + Q_ASSERT(it != parent->d()->transitions.end()); + + if (it->flags & InternalClassTransition::StructureChange) { + // A structural change. Each kind of structural change has to be recorded only once. + if ((structureChanges & it->flags) != it->flags) { + transitions.push_back(*it); + structureChanges |= it->flags; + } else { + --remainingRedundantTransitions; + } + } else if (!properties.contains(it->id)) { + // We only need the final state of the property. + properties.insert(it->id); + + // Property removal creates _two_ redundant transitions. + // We don't have to replay either, but numRedundantTransitions only records one. + if (it->flags != 0) + transitions.push_back(*it); + } else { + --remainingRedundantTransitions; + } + + child = parent->d(); + parent = child->d()->parent; + Q_ASSERT(child->d() != parent->d()); + } + } + + for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) { + switch (it->flags) { + case InternalClassTransition::NotExtensible: + child = child->d()->nonExtensible(); + continue; + case InternalClassTransition::VTableChange: + child = child->d()->changeVTable(it->vtable); + continue; + case InternalClassTransition::PrototypeChange: + child = child->d()->changePrototype(it->prototype); + continue; + case InternalClassTransition::ProtoClass: + child = child->d()->asProtoClass(); + continue; + case InternalClassTransition::Sealed: + child = child->d()->sealed(); + continue; + case InternalClassTransition::Frozen: + child = child->d()->frozen(); + continue; + default: + Q_ASSERT(it->flags != 0); + Q_ASSERT(it->flags < InternalClassTransition::StructureChange); + child = child->addMember(it->id, attributesFromFlags(it->flags)); + continue; + } + } + + return child->d(); +} + +Heap::InternalClass *InternalClass::changeMember( + PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { if (!data.isEmpty()) data.resolve(); @@ -381,7 +471,7 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert } if (data == propertyData.at(idx)) - return static_cast(this); + return this; Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); @@ -394,7 +484,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert Q_ASSERT(!propertyData.at(idx).isAccessor()); // add a dummy entry for the accessor - entry->setterIndex = newClass->size; + if (entry) + entry->setterIndex = newClass->size; e->setterIndex = newClass->size; addDummyEntry(newClass, *e); } @@ -403,7 +494,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert t.lookup = newClass; Q_ASSERT(t.lookup); - return newClass; + + return cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) @@ -413,7 +505,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) if (proto) proto->setUsedAsProto(); Q_ASSERT(prototype != proto); - Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); + Q_ASSERT(!proto || proto->internalClass->isUsedAsProto()); Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange }; temp.prototype = proto; @@ -427,8 +519,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) newClass->prototype = proto; t.lookup = newClass; - - return newClass; + return prototype ? cleanInternalClass(newClass) : newClass; } Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) @@ -449,12 +540,14 @@ Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) t.lookup = newClass; Q_ASSERT(t.lookup); Q_ASSERT(newClass->vtable); - return newClass; + return vtable == QV4::InternalClass::staticVTable() + ? newClass + : cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::nonExtensible() { - if (!extensible) + if (!isExtensible()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible}; @@ -463,7 +556,7 @@ Heap::InternalClass *InternalClass::nonExtensible() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->extensible = false; + newClass->flags |= NotExtensible; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -500,7 +593,7 @@ Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAt Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { - Transition temp = { { identifier }, nullptr, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); if (entry) { @@ -553,21 +646,23 @@ void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier) changeMember(object, identifier, Attr_Invalid); #ifndef QT_NO_DEBUG - // we didn't remove the data slot, just made it inaccessible - Q_ASSERT(object->internalClass()->size == oldClass->size); + // We didn't remove the data slot, just made it inaccessible. + // ... unless we've rebuilt the whole class. Then all the deleted properties are gone. + Q_ASSERT(object->internalClass()->numRedundantTransitions == 0 + || object->internalClass()->size == oldClass->size); #endif } Heap::InternalClass *InternalClass::sealed() { - if (isSealed) + if (isSealed()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isSealed); + Q_ASSERT(t.lookup && t.lookup->isSealed()); return t.lookup; } @@ -575,7 +670,7 @@ Heap::InternalClass *InternalClass::sealed() Scoped ic(scope, engine->newClass(this)); Heap::InternalClass *s = ic->d(); - if (!isFrozen) { // freezing also makes all properties non-configurable + if (!isFrozen()) { // freezing also makes all properties non-configurable for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -584,7 +679,7 @@ Heap::InternalClass *InternalClass::sealed() s->propertyData.set(i, attrs); } } - s->isSealed = true; + s->flags |= Sealed; t.lookup = s; return s; @@ -592,14 +687,14 @@ Heap::InternalClass *InternalClass::sealed() Heap::InternalClass *InternalClass::frozen() { - if (isFrozen) + if (isFrozen()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isFrozen); + Q_ASSERT(t.lookup && t.lookup->isFrozen()); return t.lookup; } @@ -616,7 +711,7 @@ Heap::InternalClass *InternalClass::frozen() attrs.setConfigurable(false); f->propertyData.set(i, attrs); } - f->isFrozen = true; + f->flags |= Frozen; t.lookup = f; return f; @@ -640,7 +735,7 @@ InternalClass *InternalClass::cryopreserved() bool InternalClass::isImplicitlyFrozen() const { - if (isFrozen) + if (isFrozen()) return true; for (uint i = 0; i < size; ++i) { @@ -656,7 +751,7 @@ bool InternalClass::isImplicitlyFrozen() const Heap::InternalClass *InternalClass::asProtoClass() { - if (isUsedAsProto) + if (isUsedAsProto()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass }; @@ -665,7 +760,7 @@ Heap::InternalClass *InternalClass::asProtoClass() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->isUsedAsProto = true; + newClass->flags |= UsedAsProto; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -685,7 +780,7 @@ static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic) void InternalClass::updateProtoUsage(Heap::Object *o) { - Q_ASSERT(isUsedAsProto); + Q_ASSERT(isUsedAsProto()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty); Q_ASSERT(!ic->prototype); @@ -698,6 +793,9 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) if (ic->prototype) ic->prototype->mark(stack); + if (ic->parent) + ic->parent->mark(stack); + ic->nameMap.mark(stack); } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 03ad7b827e..92ed5a9447 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -172,7 +172,7 @@ struct SharedInternalClassDataPrivate { uint size() const { return m_size; } void setSize(uint s) { m_size = s; } - PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; } + PropertyAttributes at(uint i) const { Q_ASSERT(data && i < m_alloc); return data[i]; } void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; } void mark(MarkStack *) {} @@ -197,7 +197,7 @@ struct SharedInternalClassDataPrivate { uint size() const; void setSize(uint s); - PropertyKey at(uint i); + PropertyKey at(uint i) const; void set(uint i, PropertyKey t); void mark(MarkStack *s); @@ -290,24 +290,33 @@ struct InternalClassTransition int flags; enum { // range 0-0xff is reserved for attribute changes - NotExtensible = 0x100, - VTableChange = 0x200, - PrototypeChange = 0x201, - ProtoClass = 0x202, - Sealed = 0x203, - Frozen = 0x204 + StructureChange = 0x100, + NotExtensible = StructureChange | (1 << 0), + VTableChange = StructureChange | (1 << 1), + PrototypeChange = StructureChange | (1 << 2), + ProtoClass = StructureChange | (1 << 3), + Sealed = StructureChange | (1 << 4), + Frozen = StructureChange | (1 << 5), }; bool operator==(const InternalClassTransition &other) const { return id == other.id && flags == other.flags; } bool operator<(const InternalClassTransition &other) const - { return id < other.id || (id == other.id && flags < other.flags); } + { return flags < other.flags || (flags == other.flags && id < other.id); } }; namespace Heap { struct InternalClass : Base { + enum Flag { + NotExtensible = 1 << 0, + Sealed = 1 << 1, + Frozen = 1 << 2, + UsedAsProto = 1 << 3, + }; + enum { MaxRedundantTransitions = 255 }; + ExecutionEngine *engine; const VTable *vtable; quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes @@ -323,10 +332,13 @@ struct InternalClass : Base { InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t); uint size; - bool extensible; - bool isSealed; - bool isFrozen; - bool isUsedAsProto; + quint8 numRedundantTransitions; + quint8 flags; + + bool isExtensible() const { return !(flags & NotExtensible); } + bool isSealed() const { return flags & Sealed; } + bool isFrozen() const { return flags & Frozen; } + bool isUsedAsProto() const { return flags & UsedAsProto; } void init(ExecutionEngine *engine); void init(InternalClass *other); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 2bbc6dee5a..4674b750ed 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,17 +61,52 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(Heap::InternalClass *ic) { - d()->internalClass.set(engine(), ic); - if (ic->isUsedAsProto) - ic->updateProtoUsage(d()); Q_ASSERT(ic && ic->vtable); - uint nInline = d()->vtable()->nInlineProperties; - if (ic->size <= nInline) - return; - bool hasMD = d()->memberData != nullptr; - uint requiredSize = ic->size - nInline; - if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize)) - d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData)); + Heap::Object *p = d(); + + if (ic->numRedundantTransitions < p->internalClass.get()->numRedundantTransitions) { + // IC was rebuilt. The indices are different now. We need to move everything. + + Scope scope(engine()); + + // We allocate before setting the new IC. Protect it from GC. + Scoped newIC(scope, ic); + + // Pick the members of the old IC that are still valid in the new IC. + // Order them by index in memberData (or inline data). + Scoped newMembers(scope, MemberData::allocate(scope.engine, ic->size)); + for (uint i = 0; i < ic->size; ++i) + newMembers->set(scope.engine, i, get(ic->nameMap.at(i))); + + p->internalClass.set(scope.engine, ic); + const uint nInline = p->vtable()->nInlineProperties; + + if (ic->size > nInline) + p->memberData.set(scope.engine, MemberData::allocate(ic->engine, ic->size - nInline)); + else + p->memberData.set(scope.engine, nullptr); + + const auto &memberValues = newMembers->d()->values; + for (uint i = 0; i < ic->size; ++i) + setProperty(i, memberValues[i]); + } else { + // The old indices are still the same. No need to move any values. + // We may need to re-allocate, though. + + p->internalClass.set(ic->engine, ic); + const uint nInline = p->vtable()->nInlineProperties; + if (ic->size > nInline) { + const uint requiredSize = ic->size - nInline; + if ((p->memberData ? p->memberData->values.size : 0) < requiredSize) { + p->memberData.set(ic->engine, MemberData::allocate( + ic->engine, requiredSize, p->memberData)); + } + } + } + + if (ic->isUsedAsProto()) + ic->updateProtoUsage(p); + } void Object::getProperty(const InternalClassEntry &entry, Property *p) const @@ -958,7 +993,7 @@ bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property bool Object::virtualIsExtensible(const Managed *m) { - return m->d()->internalClass->extensible; + return m->d()->internalClass->isExtensible(); } bool Object::virtualPreventExtensions(Managed *m) @@ -982,7 +1017,7 @@ bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) Heap::Object *protod = proto ? proto->d() : nullptr; if (current == protod) return true; - if (!o->internalClass()->extensible) + if (!o->internalClass()->isExtensible()) return false; Heap::Object *p = protod; while (p) { diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h index ba6829fb88..e30903239b 100644 --- a/src/qml/jsruntime/qv4propertykey_p.h +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -51,6 +51,7 @@ // #include +#include QT_BEGIN_NAMESPACE @@ -145,6 +146,10 @@ public: bool operator ==(const PropertyKey &other) const { return val == other.val; } bool operator !=(const PropertyKey &other) const { return val != other.val; } bool operator <(const PropertyKey &other) const { return val < other.val; } + inline friend uint qHash(const PropertyKey &key, uint seed) noexcept + { + return QT_PREPEND_NAMESPACE(qHash)(key.val, seed); + } }; } diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js new file mode 100644 index 0000000000..f51ab662ab --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js @@ -0,0 +1,29 @@ +function init() { + Array.prototype.doPush = Array.prototype.push +} + +function nasty() { + var sc_Vector = Array; + var push = sc_Vector.prototype.doPush; + + // Change the memberData to hold something nasty on the current internalClass + sc_Vector.prototype.doPush = 5; + + // Trigger a re-allocation of memberData + for (var i = 0; i < 256; ++i) + sc_Vector.prototype[i + "string"] = function() { return 98; } + + // Change the (new) memberData back, to hold our doPush function again. + // This should propagate a protoId change all the way up to the lookup. + sc_Vector.prototype.doPush = push; +} + +function func() { + var b = []; + + // This becomes a lookup internally, which stores protoId and a pointer + // into the memberData. It should get invalidated when memberData is re-allocated. + b.doPush(3); + + return b; +} diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml new file mode 100644 index 0000000000..460c40a750 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml @@ -0,0 +1,13 @@ +import QtQml 2.15 + +import "internalClassParentGc.js" as Foo + +QtObject { + Component.onCompleted: { + gc(); + Foo.init(); + Foo.func(); + Foo.nasty(); + objectName = Foo.func()[0]; + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2291c31895..9d5ffda180 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -390,6 +390,8 @@ private slots: void gcCrashRegressionTest(); void functionAsDefaultArgument(); + void internalClassParentGc(); + private: // static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); static void verifyContextLifetime(QQmlContextData *ctxt); @@ -9395,6 +9397,15 @@ void tst_qqmlecmascript::functionAsDefaultArgument() QCOMPARE(root->objectName(), "didRun"); } +void tst_qqmlecmascript::internalClassParentGc() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("internalClassParentGc.qml")); + QScopedPointer root(component.create()); + QVERIFY(root); + QCOMPARE(root->objectName(), "3"); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index 824fd89e5b..34da3a7c50 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -47,7 +47,7 @@ private slots: void gcStats(); void multiWrappedQObjects(); void accessParentOnDestruction(); - void clearICParent(); + void cleanInternalClasses(); }; void tst_qv4mm::gcStats() @@ -110,16 +110,41 @@ void tst_qv4mm::accessParentOnDestruction() QCOMPARE(obj->property("destructions").toInt(), 100); } -void tst_qv4mm::clearICParent() +void tst_qv4mm::cleanInternalClasses() { QV4::ExecutionEngine engine; QV4::Scope scope(engine.rootContext()); QV4::ScopedObject object(scope, engine.newObject()); + QV4::ScopedObject prototype(scope, engine.newObject()); + + // Set a prototype so that we get a unique IC. + object->setPrototypeOf(prototype); + + QV4::Scoped prevIC(scope, object->internalClass()); + QVERIFY(prevIC->d()->transitions.empty()); + + uint prevIcChainLength = 0; + for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent) + ++prevIcChainLength; + + const auto checkICCHainLength = [&]() { + uint icChainLength = 0; + for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent) + ++icChainLength; + + const uint redundant = object->internalClass()->numRedundantTransitions; + QVERIFY(redundant <= QV4::Heap::InternalClass::MaxRedundantTransitions); + + // A removal makes two transitions redundant. + QVERIFY(icChainLength <= prevIcChainLength + 2 * redundant); + }; + + const uint numTransitions = 16 * 1024; // Keep identifiers in a separate array so that we don't have to allocate them in the loop that // should test the GC on InternalClass allocations. QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject()); - for (uint i = 0; i < 16 * 1024; ++i) { + for (uint i = 0; i < numTransitions; ++i) { QV4::Scope scope(&engine); QV4::ScopedString s(scope); s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i)); @@ -130,22 +155,60 @@ void tst_qv4mm::clearICParent() object->insertMember(s, v); } - // When allocating the InternalClass objects required for deleting properties, the GC should - // eventually run and remove all but the last two. - // If we ever manage to avoid allocating the InternalClasses in the first place we will need - // to change this test. - for (uint i = 0; i < 16 * 1024; ++i) { + // There is a chain of ICs originating from the original class. + QCOMPARE(prevIC->d()->transitions.size(), 1u); + QVERIFY(prevIC->d()->transitions.front().lookup != nullptr); + + // When allocating the InternalClass objects required for deleting properties, eventually + // the IC chain gets truncated, dropping all the removed properties. + for (uint i = 0; i < numTransitions; ++i) { QV4::Scope scope(&engine); QV4::ScopedString s(scope, identifiers->get(i)); QV4::Scoped ic(scope, object->internalClass()); QVERIFY(ic->d()->parent != nullptr); - object->deleteProperty(s->toPropertyKey()); + QV4::ScopedValue val(scope, object->get(s->toPropertyKey())); + QCOMPARE(val->toNumber(), double(i)); + QVERIFY(object->deleteProperty(s->toPropertyKey())); + QVERIFY(!object->hasProperty(s->toPropertyKey())); QVERIFY(object->internalClass() != ic->d()); - QCOMPARE(object->internalClass()->parent, ic->d()); - if (ic->d()->parent == nullptr) - return; } - QFAIL("Garbage collector was not triggered by large amount of InternalClasses"); + + // None of the properties we've added are left + for (uint i = 0; i < numTransitions; ++i) { + QV4::ScopedString s(scope, identifiers->get(i)); + QVERIFY(!object->hasProperty(s->toPropertyKey())); + } + + // Also no other properties have appeared + QScopedPointer iterator(object->ownPropertyKeys(object)); + QVERIFY(!iterator->next(object).isValid()); + + checkICCHainLength(); + + // Add and remove properties until it clears all remaining redundant ones + uint i = 0; + while (object->internalClass()->numRedundantTransitions > 0) { + i = (i + 1) % numTransitions; + QV4::ScopedString s(scope, identifiers->get(i)); + QV4::ScopedValue v(scope); + v->setDouble(i); + object->insertMember(s, v); + QVERIFY(object->deleteProperty(s->toPropertyKey())); + } + + // Make sure that all dangling ICs are actually gone. + scope.engine->memoryManager->runGC(); + + // Now the GC has removed the ICs we originally added by adding properties. + QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr); + + // Same thing with redundant prototypes + for (uint i = 0; i < numTransitions; ++i) { + QV4::ScopedObject prototype(scope, engine.newObject()); + object->setPrototypeOf(prototype); // Makes previous prototype redundant + } + + checkICCHainLength(); } QTEST_MAIN(tst_qv4mm) -- cgit v1.2.3 From 47a4d10548a37a3fb8654848d78d648b47e6fb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 24 Aug 2022 15:41:29 +0200 Subject: StackLayout: Do not set size of children to (-1, -1) The item-size-hint-cache in StackLayout was not always valid when QQuickStackLayout::rearrange() was entered, so it would end up setting the size of the item to (-1, -1) Fixes: QTBUG-105899 Change-Id: I632aa18bb10b84d59af35cd3c7cb0c675d8d1692 Reviewed-by: Oliver Eftevaag (cherry picked from commit 9a4874ab7f18a54b2497f689dfd7f7a2ee0516b8) --- src/imports/layouts/qquickstacklayout.cpp | 14 ++++-- src/imports/layouts/qquickstacklayout_p.h | 1 + .../quick/qquicklayouts/data/tst_stacklayout.qml | 50 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp index 443fa0d266..bd2f93c630 100644 --- a/src/imports/layouts/qquickstacklayout.cpp +++ b/src/imports/layouts/qquickstacklayout.cpp @@ -192,10 +192,8 @@ QSizeF QQuickStackLayout::sizeHint(Qt::SizeHint whichSizeHint) const maxS = QSizeF(std::numeric_limits::infinity(), std::numeric_limits::infinity()); const int count = itemCount(); - m_cachedItemSizeHints.resize(count); for (int i = 0; i < count; ++i) { - SizeHints &hints = m_cachedItemSizeHints[i]; - QQuickStackLayout::collectItemSizeHints(itemAt(i), hints.array); + SizeHints &hints = cachedItemSizeHints(i); minS = minS.expandedTo(hints.min()); prefS = prefS.expandedTo(hints.pref()); //maxS = maxS.boundedTo(hints.max()); // Can be resized to be larger than any of its items. @@ -286,6 +284,7 @@ void QQuickStackLayout::updateLayoutItems() if (count != d->count) { d->count = count; emit countChanged(); + m_cachedItemSizeHints.resize(count); } for (int i = 0; i < count; ++i) { QQuickItem *child = itemAt(i); @@ -294,6 +293,13 @@ void QQuickStackLayout::updateLayoutItems() } } +QQuickStackLayout::SizeHints &QQuickStackLayout::cachedItemSizeHints(int index) const { + SizeHints &hints = m_cachedItemSizeHints[index]; + if (!hints.min().isValid()) + QQuickStackLayout::collectItemSizeHints(itemAt(index), hints.array); + return hints; +} + void QQuickStackLayout::rearrange(const QSizeF &newSize) { Q_D(QQuickStackLayout); @@ -305,7 +311,7 @@ void QQuickStackLayout::rearrange(const QSizeF &newSize) if (d->currentIndex == -1 || d->currentIndex >= m_cachedItemSizeHints.count()) return; - QQuickStackLayout::SizeHints &hints = m_cachedItemSizeHints[d->currentIndex]; + QQuickStackLayout::SizeHints &hints = cachedItemSizeHints(d->currentIndex); QQuickItem *item = itemAt(d->currentIndex); Q_ASSERT(item); item->setPosition(QPointF(0,0)); // ### respect alignment? diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h index e1bd84eb3c..e46c608926 100644 --- a/src/imports/layouts/qquickstacklayout_p.h +++ b/src/imports/layouts/qquickstacklayout_p.h @@ -97,6 +97,7 @@ private: mutable QVector m_cachedItemSizeHints; mutable QSizeF m_cachedSizeHints[Qt::NSizeHints]; + SizeHints &cachedItemSizeHints(int index) const; }; class QQuickStackLayoutPrivate : public QQuickLayoutPrivate diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml index 3a41bdb3e0..de335386cf 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml @@ -103,5 +103,55 @@ Item { layout.destroy() } + + Component { + id: layout_setCurrentIndex_Component + + StackLayout { + width: 200 + height: 200 + + property alias firstItem : rect + property alias secondItem: rowLayout + + Rectangle { + id: rect + color: "red" + implicitWidth: 10 + implicitHeight: 10 + } + RowLayout { + id: rowLayout + spacing: 0 + Rectangle { + color: "green" + implicitWidth: 10 + implicitHeight: 10 + Layout.fillWidth: true + Layout.fillHeight: true + } + Rectangle { + color: "blue" + implicitWidth: 10 + implicitHeight: 10 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + function test_setCurrentIndex() + { + var layout = layout_setCurrentIndex_Component.createObject(container) + compare(layout.firstItem.width, 200) + + // Invalidate the StackLayout (and its cached size hints) + layout.firstItem.implicitWidth = 42 + + layout.currentIndex = 1 + compare(layout.secondItem.width, 200) // width should not be -1 + layout.destroy() + } } } -- cgit v1.2.3 From cf9063a2ec2af9a2cd7baa70972376f21f27b76d Mon Sep 17 00:00:00 2001 From: CI Insignificant Platforms Monitor Bot Date: Tue, 6 Sep 2022 11:43:35 +0000 Subject: Blacklist 1 tests in tst_QParallelAnimationGroupJob on macos - deleteChildrenWithRunningGroup Task-number: QTBUG-106356 Change-Id: I101da0074010aa72c80e989703201bb486d6c44c Reviewed-by: CI Insignificant Platforms Monitor Bot --- tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST new file mode 100644 index 0000000000..3793debebb --- /dev/null +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[deleteChildrenWithRunningGroup] +ci macos # QTBUG-106356 -- cgit v1.2.3 From 08269077b3dc88751cbeb110e18571e27c912e98 Mon Sep 17 00:00:00 2001 From: Oliver Eftevaag Date: Tue, 2 Aug 2022 14:28:56 +0200 Subject: Flickable: let changing contentItem pos also affect the drag starting pos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling the setContentX(qreal) and setContentY(qreal) functions, the flickable would not update the drag starting position if they were called in the middle of a dragging operation. This patch makes those function update the drag starting position, so that the drag will take into account the external changes to the contentItem position, and not make any massive leaps on the next call to drag() after changing the contentItem position directly. Note that vData.pressPos and hData.pressPos are set to the x and y position of the contentItem at the beginning of a drag operation. They are unrelated to the mouse position. The bug QTBUG-104966 will be fixed from this, since QQuickItemView::setModel() calls QQuickListViewPrivate::setPosition() which calls QQuickFlickable::setContentX/Y(). The QQuickFlickable::setContentX/Y functions are public (part of the public API). They will update the timeline value for the x and y axis, which will as a result also call setViewportX/Y, which calls setX/Y for the contentItem itself. Done-with: Jan Arve Sæther Fixes: QTBUG-104966 Change-Id: Id5165e1ff37a07b94be8c1cc152e86dfcc13d1c6 Reviewed-by: Oliver Eftevaag Reviewed-by: Jan Arve Sæther (cherry picked from commit 5647527a8cde84b51fff66fc482f02435770b3dd) --- src/quick/items/qquickflickable.cpp | 28 ++++-- src/quick/items/qquickflickable_p_p.h | 4 +- .../data/contentPosWhileDragging.qml | 22 +++++ .../quick/qquickflickable/tst_qquickflickable.cpp | 107 ++++++++++++++++++++- 4 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index f4b2379979..ea39edff18 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -326,7 +326,7 @@ void QQuickFlickablePrivate::AxisData::updateVelocity() } } -void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) +void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeom) { Q_Q(QQuickFlickable); if (item == contentItem) { @@ -335,8 +335,14 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr orient |= Qt::Horizontal; if (change.yChange()) orient |= Qt::Vertical; - if (orient) + if (orient) { q->viewportMoved(orient); + const QPointF deltaMoved = item->position() - oldGeom.topLeft(); + if (hData.contentPositionChangedExternallyDuringDrag) + hData.pressPos += deltaMoved.x(); + if (vData.contentPositionChangedExternallyDuringDrag) + vData.pressPos += deltaMoved.y(); + } if (orient & Qt::Horizontal) emit q->contentXChanged(); if (orient & Qt::Vertical) @@ -796,8 +802,11 @@ void QQuickFlickable::setContentX(qreal pos) d->hData.vTime = d->timeline.time(); if (isMoving() || isFlicking()) movementEnding(true, false); - if (!qFuzzyCompare(-pos, d->hData.move.value())) + if (!qFuzzyCompare(-pos, d->hData.move.value())) { + d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging; d->hData.move.setValue(-pos); + d->hData.contentPositionChangedExternallyDuringDrag = false; + } } qreal QQuickFlickable::contentY() const @@ -814,8 +823,11 @@ void QQuickFlickable::setContentY(qreal pos) d->vData.vTime = d->timeline.time(); if (isMoving() || isFlicking()) movementEnding(false, true); - if (!qFuzzyCompare(-pos, d->vData.move.value())) + if (!qFuzzyCompare(-pos, d->vData.move.value())) { + d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging; d->vData.move.setValue(-pos); + d->vData.contentPositionChangedExternallyDuringDrag = false; + } } /*! @@ -2108,9 +2120,11 @@ void QQuickFlickable::setContentWidth(qreal w) d->contentItem->setWidth(w); d->hData.markExtentsDirty(); // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { + if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->hData.dragging) { + d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging; d->fixupMode = QQuickFlickablePrivate::Immediate; d->fixupX(); + d->hData.contentPositionChangedExternallyDuringDrag = false; } else if (!d->pressed && d->hData.fixingUp) { d->fixupMode = QQuickFlickablePrivate::ExtentChanged; d->fixupX(); @@ -2137,9 +2151,11 @@ void QQuickFlickable::setContentHeight(qreal h) d->contentItem->setHeight(h); d->vData.markExtentsDirty(); // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { + if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->vData.dragging) { + d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging; d->fixupMode = QQuickFlickablePrivate::Immediate; d->fixupY(); + d->vData.contentPositionChangedExternallyDuringDrag = false; } else if (!d->pressed && d->vData.fixingUp) { d->fixupMode = QQuickFlickablePrivate::ExtentChanged; d->fixupY(); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 1d99462e5d..0b990739fb 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -109,6 +109,7 @@ public: , fixingUp(false), inOvershoot(false), inRebound(false), moving(false), flicking(false) , dragging(false), extentsChanged(false) , explicitValue(false), minExtentDirty(true), maxExtentDirty(true) + , contentPositionChangedExternallyDuringDrag(false) , unused(0) {} @@ -169,7 +170,8 @@ public: bool explicitValue : 1; mutable bool minExtentDirty : 1; mutable bool maxExtentDirty : 1; - uint unused : 19; + bool contentPositionChangedExternallyDuringDrag : 1; + uint unused : 18; }; bool flickX(qreal velocity); diff --git a/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml new file mode 100644 index 0000000000..57a4273257 --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml @@ -0,0 +1,22 @@ +import QtQuick 2.14 + +Item { + id: root + width: 500 + height: 500 + Flickable { + anchors.centerIn: parent + width: 100 + height: 100 + clip: true + contentWidth: content.width + contentHeight: content.height + Rectangle { + id: content + width: 320 + height: width + color: "#41cd52" + radius: width/2 + } + } +} diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 9fa51da6f8..d092cd0170 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -27,10 +27,10 @@ ****************************************************************************/ #include #include +#include #include #include #include -#include #include #include #include @@ -206,6 +206,8 @@ private slots: void synchronousDrag_data(); void synchronousDrag(); void visibleAreaBinding(); + void setContentPositionWhileDragging_data(); + void setContentPositionWhileDragging(); private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); @@ -2558,6 +2560,109 @@ void tst_qquickflickable::visibleAreaBinding() // Shouldn't crash. } +void tst_qquickflickable::setContentPositionWhileDragging_data() +{ + QTest::addColumn("isHorizontal"); + QTest::addColumn("newPos"); + QTest::addColumn("newExtent"); + QTest::newRow("horizontal, setContentX") << true << 0 << -1; + QTest::newRow("vertical, setContentY") << false << 0 << -1; + QTest::newRow("horizontal, setContentWidth") << true << -1 << 200; + QTest::newRow("vertical, setContentHeight") << false << -1 << 200; +} + +void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 +{ + QFETCH(bool, isHorizontal); + QFETCH(int, newPos); + QFETCH(int, newExtent); + + QScopedPointer window(new QQuickView); + window->setSource(testFileUrl("contentPosWhileDragging.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QQuickItem *rootItem = window->rootObject(); + QVERIFY(rootItem); + + QVERIFY(window->isVisible()); + QQuickFlickable *flickable = rootItem->findChild(); + QVERIFY(flickable); + + const auto contentPos = [flickable]() -> QPoint { + return QPoint(flickable->contentX(), flickable->contentY()); + }; + const qreal threshold = + qApp->styleHints()->startDragDistance() * flickable->parentItem()->scale(); + const QPoint thresholdPnt(qRound(threshold), qRound(threshold)); + const auto flickableCenterPos = flickable->mapToScene({flickable->width() / 2, flickable->height() / 2}).toPoint(); + + // Drag the mouse until we have surpassed the mouse drag threshold and a drag is initiated + // by checking for flickable->isDragging() + QPoint pos = flickableCenterPos; + moveAndPress(window.data(), pos); + int j = 1; + QVERIFY(!flickable->isDragging()); + while (!flickable->isDragging()) { + pos = flickableCenterPos - QPoint(j, j); + QTest::mouseMove(window.data(), pos); + j++; + } + + // Now we have entered the drag state + QVERIFY(flickable->isDragging()); + QCOMPARE(flickable->contentX(), 0); + QCOMPARE(flickable->contentY(), 0); + QVERIFY(flickable->width() > 0); + QVERIFY(flickable->height() > 0); + + + const int moveLength = 50; + const QPoint unitDelta(isHorizontal ? 1 : 0, isHorizontal ? 0 : 1); + const QPoint moveDelta = unitDelta * moveLength; + + pos -= 3*moveDelta; + QTest::mouseMove(window.data(), pos); + // Should be positive because we drag in the opposite direction + QCOMPARE(contentPos(), 3 * moveDelta); + QPoint expectedContentPos; + + // Set the content item position back to zero *while dragging* (!!) + if (newPos >= 0) { + if (isHorizontal) { + flickable->setContentX(newPos); + } else { + flickable->setContentY(newPos); + } + // Continue dragging + pos -= moveDelta; + expectedContentPos = moveDelta; + } else if (newExtent >= 0) { + // ...or reduce the content size be be less than current (contentX, contentY) position + // This forces the content item to move. + expectedContentPos = moveDelta; + if (isHorizontal) { + flickable->setContentWidth(newExtent); + } else { + flickable->setContentHeight(newExtent); + } + // Assumption is that the contentItem is aligned to the bottom of the flickable + // We therefore cannot scroll/flick it further down. Drag it up towards the top instead + // (by moving mouse down). + pos += moveDelta; + } + + QTest::mouseMove(window.data(), pos); + + // Make sure that the contentItem was only dragged the delta in mouse movement since the last + // setContentX/Y() call. + QCOMPARE(contentPos(), expectedContentPos); + QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, pos); + QVERIFY(!flickable->isDragging()); +} + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" -- cgit v1.2.3 From b9d45368d778cc81509877dde143a6abe5f2e5cb Mon Sep 17 00:00:00 2001 From: CI Insignificant Platforms Monitor Bot Date: Thu, 8 Sep 2022 08:46:27 +0000 Subject: Blacklist 1 tests in tst_QQuickLoader on ubuntu-20.04 - asyncToSync1 Task-number: QTBUG-106424 Change-Id: I4728477553807db52fc695ee25797d4962545de8 Reviewed-by: CI Insignificant Platforms Monitor Bot --- tests/auto/quick/qquickloader/BLACKLIST | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickloader/BLACKLIST b/tests/auto/quick/qquickloader/BLACKLIST index a45a300607..aeb3674482 100644 --- a/tests/auto/quick/qquickloader/BLACKLIST +++ b/tests/auto/quick/qquickloader/BLACKLIST @@ -1,4 +1,5 @@ # Test fails on qemu when bound to one core, passes on real ARM # QTBUG-63049 [asyncToSync1] +ci ubuntu-20.04 # QTBUG-106424 b2qt -- cgit v1.2.3 From d9fe92c7f72c7d7610dd960ee42479a64cf26bee Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 2 Sep 2022 15:23:14 +0200 Subject: V4: Account for the guard pages when allocating stack space Previously we've assumed the whole allocation can be used, even though the first and the last page are actually not usable. This makes a difference when the size of the guard pages grows, such as on macOS, which these days has 16k pages. Add the extra guard page size to the amount of memory to be allocated in order to fix the calculation. Fixes: QTBUG-93188 Change-Id: I0ebece94449da3127e9a78a19d8a22722ad8d698 Reviewed-by: Fabian Kosmale (cherry picked from commit 826b77c8cf0ffbef4f95e7b9e72eb9dc25936657) --- src/qml/jsruntime/qv4engine.cpp | 15 +++++++++------ tests/auto/qml/qqmlecmascript/BLACKLIST | 2 -- 2 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 tests/auto/qml/qqmlecmascript/BLACKLIST diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 828d755e0e..bf5d437d10 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -300,6 +300,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) if (ok && envMaxGCStackSize > 0) m_maxGCStackSize = envMaxGCStackSize; + // We allocate guard pages around our stacks. + const size_t guardPages = 2 * WTF::pageSize(); + memoryManager = new QV4::MemoryManager(this); if (maxCallDepth == -1) { @@ -327,9 +330,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) // reserve space for the JS stack // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); + *jsStack = WTF::PageAllocation::allocate( + m_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024); @@ -337,9 +340,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); + *gcStack = WTF::PageAllocation::allocate( + m_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); { ok = false; diff --git a/tests/auto/qml/qqmlecmascript/BLACKLIST b/tests/auto/qml/qqmlecmascript/BLACKLIST deleted file mode 100644 index bd25566eef..0000000000 --- a/tests/auto/qml/qqmlecmascript/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[gcCrashRegressionTest] -macos arm -- cgit v1.2.3 From fa2d761e6f5357dfb1469e9cdb81b88e8ed5bcfd Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Fri, 9 Sep 2022 11:26:00 +0200 Subject: qqmlprivate.h: make static constexpr members c++11 compliant Newest gcc versions (latest 11 and 12) decided to be c++11 compliant for static constexpr members, meaning that they need a redundant definition at namespace scope. This behavior is required for c++11 but deprecated and not required starting with c++17. Added those definitions for all static constexpr members in qqmlprivate.h with appropriate comment. Fixes: QTBUG-106377 Change-Id: Idb3b3d38218f3a66908de259ae765db2aeb1febb Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlprivate.h | 39 ++++++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 27 ++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index a772d95d0c..173e02d22e 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -187,6 +187,23 @@ namespace QQmlPrivate = QQmlPrivate::createSingletonInstance; }; + // from https://en.cppreference.com/w/cpp/language/static: + // If a const non-inline (since C++17) static data member or a constexpr + // static data member (since C++11)(until C++17) is odr-used, a definition + // at namespace scope is still required, but it cannot have an initializer. + // + // If a static data member is declared constexpr, it is implicitly inline + // and does not need to be redeclared at namespace scope. This redeclaration + // without an initializer (formerly required as shown above) is still + // permitted, but is deprecated. + // + // TL;DR: redundant definitions for static constexpr are required in c++11 + // but deprecated in c++17. + template + constexpr CreateIntoFunction Constructors::createInto; + template + constexpr CreateSingletonFunction Constructors::createSingletonInstance; + template struct Constructors { @@ -194,6 +211,12 @@ namespace QQmlPrivate static constexpr CreateSingletonFunction createSingletonInstance = nullptr; }; + // see comment above over the Constructors definitions. + template + constexpr CreateIntoFunction Constructors::createInto; + template + constexpr CreateSingletonFunction Constructors::createSingletonInstance; + template::value> struct ExtendedType; @@ -205,6 +228,12 @@ namespace QQmlPrivate static constexpr const QMetaObject *staticMetaObject = nullptr; }; + // see comment above over the Constructors definitions. + template + constexpr const CreateParentFunction ExtendedType::createParent; + template + constexpr const QMetaObject* ExtendedType::staticMetaObject; + // If it's not void, we actually want an error if the ctor or the metaobject is missing. template struct ExtendedType @@ -213,6 +242,12 @@ namespace QQmlPrivate static constexpr const QMetaObject *staticMetaObject = &T::staticMetaObject; }; + // see comment above over the Constructors definitions. + template + constexpr const CreateParentFunction ExtendedType::createParent; + template + constexpr const QMetaObject* ExtendedType::staticMetaObject; + template struct StaticCastSelectorClass { @@ -577,6 +612,10 @@ namespace QQmlPrivate static constexpr bool Value = false; }; + // see comment above over the Constructors definitions. + template + constexpr bool QmlSingleton::Value; + template struct QmlSingleton> { diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 48cf20cf38..f3c8d87a8f 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -338,6 +339,7 @@ private slots: void hangOnWarning(); void ambiguousContainingType(); + void staticConstexprMembers(); private: QQmlEngine engine; @@ -5891,6 +5893,31 @@ void tst_qqmllanguage::ambiguousContainingType() QVERIFY(!o.isNull()); } } + void tst_qqmllanguage::staticConstexprMembers() { + // this tests if the symbols are correclty defined for c++11 (gcc 11 and 12), and should + // not have any linker errors + using T = QObject; + using T2 = QObject; + + auto f1 = QQmlPrivate::Constructors::createSingletonInstance; + auto f2 = QQmlPrivate::Constructors::createSingletonInstance; + auto f3 = QQmlPrivate::Constructors::createInto; + + auto f4 = QQmlPrivate::ExtendedType::createParent; + auto f5 = QQmlPrivate::ExtendedType::createParent; + auto f6 = QQmlPrivate::ExtendedType::staticMetaObject; + + auto f7 = QQmlPrivate::QmlSingleton::Value; + + Q_UNUSED(f1); + Q_UNUSED(f2); + Q_UNUSED(f3); + Q_UNUSED(f3); + Q_UNUSED(f4); + Q_UNUSED(f5); + Q_UNUSED(f6); + Q_UNUSED(f7); + } QTEST_MAIN(tst_qqmllanguage) -- cgit v1.2.3 From 71fb661ef8b4329a3df39750f9c72c024ca8a060 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 21 Jun 2022 13:44:36 +0200 Subject: Fix fractional scaling of text in Qt Quick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As opposed to the raster engine, in Qt Quick we are using unscaled positions for the glyphs and using the vertex shader to scale these after the fact. However, when picking the correct subpixel rendering for each glyph, we would use the unscaled position's fractional part, meaning that we essentially rendered the glyphs at the wrong subpixel position. This was especially visible when doing fractional scaling, e.g. 125%. Instead, we need to ensure that we pick the rendering at the on-screen subpixel position. [ChangeLog][QtQuick][Text] Fixed a kerning issue with native-rendered text when there is a fractional system scale factor set. Fixes: QTBUG-101008 Change-Id: Ic0f94d8b4ca5998dca638bdf7e2a16306d92a926 Reviewed-by: Tor Arne Vestbø (cherry picked from commit 97cc7023ce8b8d903351a2e13f515592854c56fc) --- src/quick/scenegraph/qsgdefaultglyphnode_p.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index 6f3205f526..61d542fbcb 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -818,8 +818,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p, QTextureGlyphCache *cache = glyphCache(); QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); - cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(), - fixedPointPositions.data()); + cache->populate(fontD->fontEngine, + glyphIndexes.size(), + glyphIndexes.constData(), + fixedPointPositions.data(), + true); cache->fillInPendingGlyphs(); int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat()); @@ -841,7 +844,7 @@ void QSGTextMaskMaterial::populate(const QPointF &p, QPointF glyphPosition = glyphPositions.at(i) + position; QFixed subPixelPosition; if (supportsSubPixelPositions) - subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x())); + subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x() * glyphCacheScaleX)); QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition); const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); -- cgit v1.2.3 From c6d8d037828701622d5664b5141e1ffe18065097 Mon Sep 17 00:00:00 2001 From: CI Insignificant Platforms Monitor Bot Date: Mon, 5 Sep 2022 23:43:18 +0000 Subject: Blacklist 1 tests in tst_qquickflickable on macos - setContentPositionWhileDragging Task-number: QTBUG-106278 Change-Id: I572c34aaf632929a81bb2ba8afa4d7549ed5355b Reviewed-by: CI Insignificant Platforms Monitor Bot --- tests/auto/quick/qquickflickable/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/quick/qquickflickable/BLACKLIST diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST new file mode 100644 index 0000000000..636aef4904 --- /dev/null +++ b/tests/auto/quick/qquickflickable/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[setContentPositionWhileDragging] +ci macos # QTBUG-106278 -- cgit v1.2.3 From 8d6beb794058050fa750de74956d893db2491d3e Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Wed, 14 Sep 2022 15:39:08 +0200 Subject: Text: Re-layout the text when a alignment is set and the height grows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the top alignment (default) is used, a growing height can be ignored in some situations and no re-layout is needed. But once the alignment has been changed, the re-layout needs to happen, as otherwise not all anchors are updated correctly. Fixes: QTBUG-106594 Change-Id: I9ab9999f49331aadd3bb8247d520288c40b51b21 Reviewed-by: Qt CI Bot Reviewed-by: Jan Arve Sæther (cherry picked from commit 4933bd6351feec988746af647c391c46483f049a) Reviewed-by: Dominik Holland --- src/quick/items/qquicktext.cpp | 2 +- tests/auto/quick/qquicktext/tst_qquicktext.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 844a25b010..a4c7cedbac 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2409,7 +2409,7 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo goto geomChangeDone; if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed - if (newGeometry.height() > oldGeometry.height()) { + if (!verticalPositionChanged && newGeometry.height() > oldGeometry.height()) { if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { // Height is adequate and growing, and it wasn't 0 previously. goto geomChangeDone; diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index ff191bbc7f..d6d3178f95 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -3478,6 +3478,30 @@ void tst_qquicktext::fontSizeMode() myText->setElideMode(QQuickText::ElideNone); QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + // Growing height needs to update the baselineOffset when AlignBottom is used + // and text is NOT wrapped + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::Fit); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + int baselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() * 2); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QVERIFY(myText->baselineOffset() > baselineOffset); + + // Growing height needs to update the baselineOffset when AlignBottom is used + // and the text is wrapped + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::Fit); + myText->setWrapMode(QQuickText::NoWrap); + myText->resetMaximumLineCount(); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + baselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() * 2); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QVERIFY(myText->baselineOffset() > baselineOffset); } void tst_qquicktext::fontSizeModeMultiline_data() -- cgit v1.2.3 From 8dd38f4261e496026a9065ceb4ba4217d4bd64c2 Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Tue, 6 Sep 2022 16:01:28 +0200 Subject: QQmlVMEMetaObjectEndpoint: ensure property cache before accessing it Otherwise the property cache might be null and lead to segmentation faults, e.g. when declaring aliases in qml. Fixes: QTBUG-106256 Change-Id: I568c973e1ba00531fbe22a41e2c6c3c09dfc2f02 Reviewed-by: Ulf Hermann (cherry picked from commit e0a00a691963e2c684f4dd857d042a3ffbebe959) Reviewed-by: Fabian Kosmale --- src/qml/qml/qqmlvmemetaobject.cpp | 5 +---- tests/auto/qml/qqmllanguage/data/ComponentType.qml | 8 ++++++++ .../qqmllanguage/data/bindingAliasToComponentUrl.qml | 11 +++++++++++ .../data/bindingAliasToComponentUrl2.qml | 11 +++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 20 ++++++++++++++++++++ 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/ComponentType.qml create mode 100644 tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml create mode 100644 tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 11edeb0914..083dad6afd 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -235,13 +235,10 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() if (!target) return; - QQmlData *targetDData = QQmlData::get(target, /*create*/false); - if (!targetDData) - return; QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); int coreIndex = encodedIndex.coreIndex(); int valueTypeIndex = encodedIndex.valueTypeIndex(); - const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); + const QQmlPropertyData *pd = QQmlData::ensurePropertyCache(qmlEngine(target), target)->property(coreIndex); if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) { // deep alias QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine()); diff --git a/tests/auto/qml/qqmllanguage/data/ComponentType.qml b/tests/auto/qml/qqmllanguage/data/ComponentType.qml new file mode 100644 index 0000000000..ad0dad455c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/ComponentType.qml @@ -0,0 +1,8 @@ +import QtQml 2 + +Component { + id: componentRoot + QtObject { + objectName: "enclosed" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml new file mode 100644 index 0000000000..7e10553ae7 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml @@ -0,0 +1,11 @@ +import QtQuick 2 + +Item { + id: root + Component { + id: accessibleNormal + Item {} + } + property alias accessibleNormalUrl: accessibleNormal.url + property url urlClone: root.accessibleNormalUrl // crashes qml utility +} diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml new file mode 100644 index 0000000000..899b7aca37 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml @@ -0,0 +1,11 @@ +import QtQuick 2 +Item { + id: root + Component { + id: accessibleNormal + ComponentType { + id: inaccessibleNormal + } + } + property alias accessibleNormalProgress: accessibleNormal.progress +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f3c8d87a8f..1be1533b63 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -340,6 +340,7 @@ private slots: void ambiguousContainingType(); void staticConstexprMembers(); + void bindingAliasToComponentUrl(); private: QQmlEngine engine; @@ -5919,6 +5920,25 @@ void tst_qqmllanguage::ambiguousContainingType() Q_UNUSED(f7); } +void tst_qqmllanguage::bindingAliasToComponentUrl() +{ + QQmlEngine engine; + { + QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer object(component.create()); + QVERIFY(object); + QCOMPARE(object->property("accessibleNormalUrl"), object->property("urlClone")); + } + { + QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl2.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer object(component.create()); + QVERIFY(object); + QCOMPARE(object->property("accessibleNormalProgress"), QVariant(1.0)); + } +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3 From cecf9b52904ab790e1a531698e9c5e33585227f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Fri, 16 Sep 2022 09:28:39 +0200 Subject: Adjust baselineOffset correctly when fontSizeMode == HorizontalFit This caught my attention while reviewing a related fix (4933bd6351feec988746af647c391c46483f049a) Task-number: QTBUG-106594 Change-Id: Ie8c7fb2d9e504b14a924eca72a87295d8764cf39 Reviewed-by: Dominik Holland (cherry picked from commit a7c8bd27e390db9d0d401f9008ceb4130da53d6f) --- src/quick/items/qquicktext.cpp | 32 ++++++++++++++------------ tests/auto/quick/qquicktext/tst_qquicktext.cpp | 16 +++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index a4c7cedbac..5b333d24b4 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2409,21 +2409,23 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo goto geomChangeDone; if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed - if (!verticalPositionChanged && newGeometry.height() > oldGeometry.height()) { - if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { - // Height is adequate and growing, and it wasn't 0 previously. - goto geomChangeDone; - } - if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing. - goto geomChangeDone; - } else if (newGeometry.height() < oldGeometry.height()) { - if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height. - goto geomChangeDone; - - if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count. - && d->elideMode != QQuickText::ElideRight - && !(d->maximumLineCountValid && d->widthExceeded)) { - goto geomChangeDone; + if (!verticalPositionChanged) { + if (newGeometry.height() > oldGeometry.height()) { + if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { + // Height is adequate and growing, and it wasn't 0 previously. + goto geomChangeDone; + } + if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing. + goto geomChangeDone; + } else if (newGeometry.height() < oldGeometry.height()) { + if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height. + goto geomChangeDone; + + if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count. + && d->elideMode != QQuickText::ElideRight + && !(d->maximumLineCountValid && d->widthExceeded)) { + goto geomChangeDone; + } } } } else if (!heightChanged && widthMaximum) { diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index d6d3178f95..03570b43ce 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -3502,6 +3502,22 @@ void tst_qquicktext::fontSizeMode() myText->setHeight(myText->height() * 2); QVERIFY(QQuickTest::qWaitForItemPolished(myText)); QVERIFY(myText->baselineOffset() > baselineOffset); + + // Check baselineOffset for the HorizontalFit case + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::HorizontalFit); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QSignalSpy baselineOffsetSpy(myText, SIGNAL(baselineOffsetChanged(qreal))); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + const qreal oldBaselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() + 42); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QCOMPARE(baselineOffsetSpy.count(), 1); + QCOMPARE(myText->baselineOffset(), oldBaselineOffset + 42); + myText->setHeight(myText->height() - 42); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QCOMPARE(baselineOffsetSpy.count(), 2); + QCOMPARE(myText->baselineOffset(), oldBaselineOffset); } void tst_qquicktext::fontSizeModeMultiline_data() -- cgit v1.2.3