From 6a8d4f614171444a77a127dc2e22f295d1ffabe2 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 29 Apr 2019 13:05:03 +0200 Subject: Don't wrap the attachedProperties function into a template Otherwise it gets a separate address for each CU in which the template is instantiated. We want to use the address as key to the attached properties, though. Fixes: QTBUG-75385 Change-Id: Iaec82db116a032f7cb1d40670bb47fdf610664a2 Reviewed-by: Mitch Curtis Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlprivate.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index fa05b3fe19..ae84803648 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -190,16 +190,13 @@ namespace QQmlPrivate template class AttachedPropertySelector { - static inline QObject *attachedProperties(QObject *obj) { - return T::qmlAttachedProperties(obj); - } template static inline const QMetaObject *attachedPropertiesMetaObject(ReturnType *(*)(QObject *)) { return &ReturnType::staticMetaObject; } public: static inline QQmlAttachedPropertiesFunc func() { - return &attachedProperties; + return QQmlAttachedPropertiesFunc(&T::qmlAttachedProperties); } static inline const QMetaObject *metaObject() { return attachedPropertiesMetaObject(&T::qmlAttachedProperties); -- cgit v1.2.3 From 4a5259929245dd204dee6061c614b369441f6724 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Thu, 25 Apr 2019 16:48:39 +0300 Subject: QQuickTextControl: Emit cursorPositionChanged() when handling IM event QQuickTextControl and consequently QQuickTextEdit did not emit cursorPositionChanged() signal when cursor position was changed by a QInputMethodEvent with a QInputMethodEvent::Selection attribute. This is especially important on Android because QAndroidInputContext uses such events extensively to move the cursor and also relies on cursorPositionChanged() signal being emitted by the focus object. If the signal is not emitted, QAndroidInputContext does not notify the virtual keyboard about cursor position change which results in various glitches. Change-Id: I46bef6185463d11507b1b96435fdc056bbe951f2 Reviewed-by: Shawn Rutledge Reviewed-by: Richard Moe Gustavsen --- src/quick/items/qquicktextcontrol.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 38ca7283b4..1dd593cc1a 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -1297,6 +1297,7 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) || e->preeditString() != cursor.block().layout()->preeditAreaText() || e->replacementLength() > 0; bool forceSelectionChanged = false; + int oldCursorPos = cursor.position(); cursor.beginEditBlock(); if (isGettingInput) { @@ -1365,6 +1366,8 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor); if (cursor_d) cursor_d->setX(); + if (cursor.position() != oldCursorPos) + emit q->cursorPositionChanged(); q->updateCursorRectangle(oldPreeditCursor != preeditCursor || forceSelectionChanged || isGettingInput); selectionChanged(forceSelectionChanged); } -- cgit v1.2.3 From 1db6782d6081fa54e7c951c21ac7c8953d2c9bfb Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 1 Apr 2019 13:10:35 +0200 Subject: QQuickTableView: optimize forceLayout() to start updating from the root Change-Id: Ib2f195780415836ebb03c151a6586fd7b0fb77b8 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 8f5130fc17..9e795f3c8c 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -770,13 +770,12 @@ void QQuickTableViewPrivate::forceLayout() scheduleRebuildTable(rebuildOptions); - if (polishing) { + auto rootView = rootSyncView(); + const bool updated = rootView->d_func()->updateTableRecursive(); + if (!updated) { qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!"; - q_func()->polish(); - return; + rootView->polish(); } - - updatePolish(); } void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest() -- cgit v1.2.3 From 55c497ecf47894c602c1b4a08099ac63546b9260 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 2 Apr 2019 09:14:17 +0200 Subject: QQuickTableView: protect fixup() from being called while resizing the content view Calling the base class implementation of fixup might move the content view and start animations etc, which will cause glitches. So ensure we don't do this when we adjust the content size internally. Change-Id: I214a6ae2da0c21fd733ea884bccb5e77fc554615 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 17 ++++++++++++++++- src/quick/items/qquicktableview_p_p.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 9e795f3c8c..ae1daccb39 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -616,6 +616,7 @@ void QQuickTableViewPrivate::updateContentWidth() Q_Q(QQuickTableView); if (syncHorizontally) { + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentWidth(syncView->contentWidth()); return; } @@ -632,6 +633,8 @@ void QQuickTableViewPrivate::updateContentWidth() const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; const qreal estimatedWidth = loadedTableOuterRect.right() + estimatedRemainingWidth; + + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentWidth(estimatedWidth); } @@ -640,6 +643,7 @@ void QQuickTableViewPrivate::updateContentHeight() Q_Q(QQuickTableView); if (syncVertically) { + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentHeight(syncView->contentHeight()); return; } @@ -656,6 +660,8 @@ void QQuickTableViewPrivate::updateContentHeight() const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing; const qreal estimatedHeight = loadedTableOuterRect.bottom() + estimatedRemainingHeight; + + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentHeight(estimatedHeight); } @@ -1924,8 +1930,17 @@ bool QQuickTableViewPrivate::updateTable() void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent) { - if (scheduledRebuildOptions || rebuildState != RebuildState::Done) + if (inUpdateContentSize) { + // We update the content size dynamically as we load and unload edges. + // Unfortunately, this also triggers a call to this function. The base + // implementation will do things like start a momentum animation or move + // the content view somewhere else, which causes glitches. This can + // especially happen if flicking on one of the syncView children, which triggers + // an update to our content size. In that case, the base implementation don't know + // that the view is being indirectly dragged, and will therefore do strange things as + // it tries to 'fixup' the geometry. So we use a guard to prevent this from happening. return; + } QQuickFlickablePrivate::fixup(data, minExtent, maxExtent); } diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 7f2aee9105..e2a413db2c 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -272,6 +272,7 @@ public: bool syncHorizontally = false; bool inSetLocalViewportPos = false; bool inSyncViewportPosRecursive = false; + bool inUpdateContentSize = false; QJSValue rowHeightProvider; QJSValue columnWidthProvider; -- cgit v1.2.3 From b56273a77872ea0516767d4fbdb846a8b5103da4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 10 Apr 2019 16:03:52 +0200 Subject: Add features for the remaining QML model types The table and object models should be configurable, too, and Package conceptually belongs to the delegate model. Change-Id: Id928f14c5b378b6e7ffcbb98039192fa7c375fa7 Reviewed-by: Simon Hausmann --- src/qmlmodels/configure.json | 13 ++++++++ src/qmlmodels/qmlmodels.pro | 54 +++++++++++++++++++------------- src/qmlmodels/qqmlinstantiator_p.h | 2 ++ src/qmlmodels/qqmlinstantiator_p_p.h | 2 ++ src/qmlmodels/qqmlmodelsmodule.cpp | 31 +++++++++++++----- src/qmlmodels/qqmlobjectmodel_p.h | 2 ++ src/qmlmodels/qqmltableinstancemodel_p.h | 2 ++ src/qmlmodels/qqmltablemodel_p.h | 2 ++ src/qmlmodels/qqmltablemodelcolumn_p.h | 2 ++ src/qmlmodels/qquickpackage_p.h | 3 ++ src/quick/configure.json | 1 + 11 files changed, 85 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/qmlmodels/configure.json b/src/qmlmodels/configure.json index 2aa8a50e69..bfe7d538ec 100644 --- a/src/qmlmodels/configure.json +++ b/src/qmlmodels/configure.json @@ -6,6 +6,12 @@ ], "features": { + "qml-object-model" : { + "label": "QML list model", + "purpose": "Provides the ObjectModel and Instantiator QML types.", + "section": "QML", + "output": [ "privateFeature" ] + }, "qml-list-model": { "label": "QML list model", "purpose": "Provides the ListModel QML type.", @@ -16,6 +22,13 @@ "label": "QML delegate model", "purpose": "Provides the DelegateModel QML type.", "section": "QML", + "condition": "features.qml-object-model", + "output": [ "privateFeature" ] + }, + "qml-table-model": { + "label": "QML table model", + "purpose": "Provides the TableModel QML type.", + "section": "QML", "output": [ "privateFeature" ] } }, diff --git a/src/qmlmodels/qmlmodels.pro b/src/qmlmodels/qmlmodels.pro index 84f87f8bb1..7d1d9bdf67 100644 --- a/src/qmlmodels/qmlmodels.pro +++ b/src/qmlmodels/qmlmodels.pro @@ -5,30 +5,36 @@ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FORE HEADERS += \ $$PWD/qqmlchangeset_p.h \ - $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h \ - $$PWD/qqmllistaccessor_p.h \ - $$PWD/qqmllistcompositor_p.h \ $$PWD/qqmlmodelsmodule_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qqmltableinstancemodel_p.h \ - $$PWD/qqmltablemodel_p.h \ - $$PWD/qqmltablemodelcolumn_p.h \ - $$PWD/qquickpackage_p.h \ $$PWD/qtqmlmodelsglobal_p.h \ - $$PWD/qtqmlmodelsglobal.h \ + $$PWD/qtqmlmodelsglobal.h SOURCES += \ $$PWD/qqmlchangeset.cpp \ - $$PWD/qqmlinstantiator.cpp \ - $$PWD/qqmllistaccessor.cpp \ - $$PWD/qqmllistcompositor.cpp \ - $$PWD/qqmlmodelsmodule.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qqmltableinstancemodel.cpp \ - $$PWD/qqmltablemodel.cpp \ - $$PWD/qqmltablemodelcolumn.cpp \ - $$PWD/qquickpackage.cpp + $$PWD/qqmlmodelsmodule.cpp + +qtConfig(qml-object-model) { + SOURCES += \ + $$PWD/qqmlinstantiator.cpp \ + $$PWD/qqmlobjectmodel.cpp + + HEADERS += \ + $$PWD/qqmlinstantiator_p.h \ + $$PWD/qqmlinstantiator_p_p.h \ + $$PWD/qqmlobjectmodel_p.h +} + +qtConfig(qml-table-model) { + SOURCES += \ + $$PWD/qqmltableinstancemodel.cpp \ + $$PWD/qqmltablemodel.cpp \ + $$PWD/qqmltablemodelcolumn.cpp + + HEADERS += \ + $$PWD/qqmltableinstancemodel_p.h \ + $$PWD/qqmltablemodel_p.h \ + $$PWD/qqmltablemodelcolumn_p.h +} qtConfig(qml-list-model) { SOURCES += \ @@ -45,13 +51,19 @@ qtConfig(qml-delegate-model) { SOURCES += \ $$PWD/qqmladaptormodel.cpp \ $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmldelegatecomponent.cpp + $$PWD/qqmldelegatecomponent.cpp \ + $$PWD/qqmllistaccessor.cpp \ + $$PWD/qqmllistcompositor.cpp \ + $$PWD/qquickpackage.cpp HEADERS += \ $$PWD/qqmladaptormodel_p.h \ $$PWD/qqmldelegatemodel_p.h \ $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmldelegatecomponent_p.h + $$PWD/qqmldelegatecomponent_p.h \ + $$PWD/qqmllistaccessor_p.h \ + $$PWD/qqmllistcompositor_p.h \ + $$PWD/qquickpackage_p.h } load(qt_module) diff --git a/src/qmlmodels/qqmlinstantiator_p.h b/src/qmlmodels/qqmlinstantiator_p.h index 8b00a1e033..87accc304f 100644 --- a/src/qmlmodels/qqmlinstantiator_p.h +++ b/src/qmlmodels/qqmlinstantiator_p.h @@ -55,6 +55,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_object_model); + QT_BEGIN_NAMESPACE class QQmlInstantiatorPrivate; diff --git a/src/qmlmodels/qqmlinstantiator_p_p.h b/src/qmlmodels/qqmlinstantiator_p_p.h index bf153d8723..33cc2613e5 100644 --- a/src/qmlmodels/qqmlinstantiator_p_p.h +++ b/src/qmlmodels/qqmlinstantiator_p_p.h @@ -57,6 +57,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_object_model); + QT_BEGIN_NAMESPACE class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate diff --git a/src/qmlmodels/qqmlmodelsmodule.cpp b/src/qmlmodels/qqmlmodelsmodule.cpp index 989fec9b7d..2db5dd834f 100644 --- a/src/qmlmodels/qqmlmodelsmodule.cpp +++ b/src/qmlmodels/qqmlmodelsmodule.cpp @@ -39,19 +39,26 @@ #include "qqmlmodelsmodule_p.h" #include + +#if QT_CONFIG(itemmodel) #include +#endif #if QT_CONFIG(qml_list_model) #include #endif #if QT_CONFIG(qml_delegate_model) #include #include +#include #endif +#if QT_CONFIG(qml_object_model) #include +#include +#endif +#if QT_CONFIG(qml_table_model) #include #include -#include -#include +#endif QT_BEGIN_NAMESPACE @@ -60,8 +67,10 @@ QT_BEGIN_NAMESPACE void QQmlModelsModule::registerQmlTypes() { // Don't add anything here. These are only for backwards compatibility. +#if QT_CONFIG(qml_object_model) qmlRegisterType("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1 qmlRegisterType(); +#endif } void QQmlModelsModule::registerQuickTypes() @@ -70,18 +79,20 @@ void QQmlModelsModule::registerQuickTypes() const char uri[] = "QtQuick"; +#if QT_CONFIG(qml_object_model) qmlRegisterType(uri, 2, 1, "Instantiator"); qmlRegisterType(); + qmlRegisterType(uri, 2, 0, "VisualItemModel"); +#endif #if QT_CONFIG(qml_list_model) qmlRegisterType(uri, 2, 0, "ListElement"); qmlRegisterCustomType(uri, 2, 0, "ListModel", new QQmlListModelParser); #endif - qmlRegisterType(uri, 2, 0, "Package"); #if QT_CONFIG(qml_delegate_model) qmlRegisterType(uri, 2, 0, "VisualDataModel"); qmlRegisterType(uri, 2, 0, "VisualDataGroup"); + qmlRegisterType(uri, 2, 0, "Package"); #endif - qmlRegisterType(uri, 2, 0, "VisualItemModel"); } #endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -97,15 +108,17 @@ void QQmlModelsModule::defineModule() #if QT_CONFIG(qml_delegate_model) qmlRegisterType(uri, 2, 1, "DelegateModel"); qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); + qmlRegisterType(uri, 2, 14, "Package"); #endif +#if QT_CONFIG(qml_object_model) qmlRegisterType(uri, 2, 1, "ObjectModel"); qmlRegisterType(uri, 2, 3, "ObjectModel"); - - qmlRegisterType(uri, 2, 2, "ItemSelectionModel"); - - qmlRegisterType(uri, 2, 14, "Package"); qmlRegisterType(uri, 2, 14, "Instantiator"); qmlRegisterType(); +#endif +#if QT_CONFIG(itemmodel) + qmlRegisterType(uri, 2, 2, "ItemSelectionModel"); +#endif } void QQmlModelsModule::defineLabsModule() @@ -117,8 +130,10 @@ void QQmlModelsModule::defineLabsModule() qmlRegisterType(uri, 1, 0, "DelegateChooser"); qmlRegisterType(uri, 1, 0, "DelegateChoice"); #endif +#if QT_CONFIG(qml_table_model) qmlRegisterType(uri, 1, 0, "TableModel"); qmlRegisterType(uri, 1, 0, "TableModelColumn"); +#endif } QT_END_NAMESPACE diff --git a/src/qmlmodels/qqmlobjectmodel_p.h b/src/qmlmodels/qqmlobjectmodel_p.h index 99bfd86269..78a5615ae2 100644 --- a/src/qmlmodels/qqmlobjectmodel_p.h +++ b/src/qmlmodels/qqmlobjectmodel_p.h @@ -56,6 +56,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_object_model); + QT_BEGIN_NAMESPACE class QObject; diff --git a/src/qmlmodels/qqmltableinstancemodel_p.h b/src/qmlmodels/qqmltableinstancemodel_p.h index 20331df5cc..ce5a37bc98 100644 --- a/src/qmlmodels/qqmltableinstancemodel_p.h +++ b/src/qmlmodels/qqmltableinstancemodel_p.h @@ -54,6 +54,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_table_model); + QT_BEGIN_NAMESPACE class QQmlTableInstanceModel; diff --git a/src/qmlmodels/qqmltablemodel_p.h b/src/qmlmodels/qqmltablemodel_p.h index 114b162e5c..b3c0cc2848 100644 --- a/src/qmlmodels/qqmltablemodel_p.h +++ b/src/qmlmodels/qqmltablemodel_p.h @@ -59,6 +59,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_table_model); + QT_BEGIN_NAMESPACE class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus diff --git a/src/qmlmodels/qqmltablemodelcolumn_p.h b/src/qmlmodels/qqmltablemodelcolumn_p.h index d125f8bb16..0b6478ce38 100644 --- a/src/qmlmodels/qqmltablemodelcolumn_p.h +++ b/src/qmlmodels/qqmltablemodelcolumn_p.h @@ -56,6 +56,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_table_model); + QT_BEGIN_NAMESPACE class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableModelColumn : public QObject diff --git a/src/qmlmodels/qquickpackage_p.h b/src/qmlmodels/qquickpackage_p.h index 122c7fcb30..97f7818fee 100644 --- a/src/qmlmodels/qquickpackage_p.h +++ b/src/qmlmodels/qquickpackage_p.h @@ -52,6 +52,9 @@ // #include +#include + +QT_REQUIRE_CONFIG(qml_delegate_model); QT_BEGIN_NAMESPACE diff --git a/src/quick/configure.json b/src/quick/configure.json index 70fe6d2129..4098db87aa 100644 --- a/src/quick/configure.json +++ b/src/quick/configure.json @@ -113,6 +113,7 @@ "label": "TableView item", "purpose": "Provides the TableView item.", "section": "Qt Quick", + "condition": "features.qml-table-model", "output": [ "privateFeature" ] -- cgit v1.2.3 From 6bce577535d05635a8adef1355ffeddeb5d3c65a Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 9 Apr 2019 14:55:27 +0200 Subject: QQuickTableView: check if we need to rebuild when syncing with syncView Since different tables can have different sized models, it can also happen, as the views are being flicked around, that some views temporarily end up with no visible rows and columns in the viewport. When that happens, we continually check if the columns should become visible again, and if so, schedule a rebuild. Change-Id: Ic84e47fd5d7968c1f1408eb122e38fa841e7aec7 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index ae1daccb39..e169ac4aec 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -2025,13 +2025,11 @@ void QQuickTableViewPrivate::syncWithPendingChanges() Q_Q(QQuickTableView); viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height()); - // Sync rebuild options first, in case we schedule a rebuild from one of the - // other sync calls above. If so, we need to start a new rebuild from the top. - syncRebuildOptions(); - syncModel(); syncDelegate(); syncSyncView(); + + syncRebuildOptions(); } void QQuickTableViewPrivate::syncRebuildOptions() @@ -2121,7 +2119,6 @@ void QQuickTableViewPrivate::syncSyncView() assignedSyncView->d_func()->syncChildren.append(q); scheduledRebuildOptions |= RebuildOption::ViewportOnly; - q->polish(); } syncView = assignedSyncView; @@ -2134,6 +2131,21 @@ void QQuickTableViewPrivate::syncSyncView() q->setColumnSpacing(syncView->columnSpacing()); if (syncVertically) q->setRowSpacing(syncView->rowSpacing()); + + if (syncView && loadedItems.isEmpty() && !tableSize.isEmpty()) { + // When we have a syncView, we can sometimes temporarily end up with no loaded items. + // This can happen if the syncView has a model with more rows or columns than us, in + // which case the viewport can end up in a place where we have no rows or columns to + // show. In that case, check now if the viewport has been flicked back again, and + // that we can rebuild the table with a visible top-left cell. + const auto syncView_d = syncView->d_func(); + if (!syncView_d->loadedItems.isEmpty()) { + if (syncHorizontally && syncView_d->leftColumn() <= tableSize.width() - 1) + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly; + else if (syncVertically && syncView_d->topRow() <= tableSize.height() - 1) + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly; + } + } } void QQuickTableViewPrivate::connectToModel() -- cgit v1.2.3 From 624aff9125951229c98880afd5003fcb474d1d69 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 3 May 2019 08:51:09 +0200 Subject: Improve Context2D's line dash-related documentation Fix spelling errors, punctuation, and links. Change-Id: I79154d0da6acf403903f102655d5dcb64240b137 Reviewed-by: Shawn Rutledge --- src/quick/items/context2d/qquickcontext2d.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 57e697bdd1..1c7f3ecc6f 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -2001,7 +2001,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionO \since QtQuick 2.11 Returns an array of qreals representing the dash pattern of the line. - \sa setLineDash() + \sa setLineDash(), lineDashOffset */ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { @@ -2023,7 +2023,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::Fun /*! \qmlmethod QtQuick::Context2D::setLineDash(array pattern) \since QtQuick 2.11 - Sets the dash pattern to the given pattern + Sets the dash pattern to the given pattern. \a pattern a list of numbers that specifies distances to alternately draw a line and a gap. @@ -2042,7 +2042,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::Fun \endcode \endtable - \sa setLineDash + \sa getLineDash(), lineDashOffset */ QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) { @@ -2084,8 +2084,10 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::Fun \qmlproperty real QtQuick::Context2D::lineDashOffset \since QtQuick 2.11 - Holds the current line dash offset - The default line dash ofset value is 0 + Holds the current line dash offset. + The default line dash offset value is \c 0. + + \sa getLineDash(), setLineDash() */ QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { -- cgit v1.2.3 From d2c089eb11d3ad6a9917efde8f13b36931b35baf Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Mon, 29 Apr 2019 10:46:02 +0200 Subject: Add Q_TRACE calls to QtQml for QML profiler trace points This adds tracepoints for LTTng/ETW at the positions that are also used by the QML profiler within QtQml. I.e. with the tracepoints here, you'll see which QML function is being executed which is already quite helpful. This will allow us to bridge the gap between C++ and QML when tracing with LTTng/ETW. Additionally, you'll also be able to see kernel tracepoints which bridges another gap. Combined, this approach can give much deeper insights into what the (embedded) system is doing compared to just looking at the QML profiler alone. Change-Id: Ia8f71bf6d44b7f51c3c5aaa38f032675604aeca6 Reviewed-by: Ulf Hermann Reviewed-by: Rafael Roquetto --- src/qml/jsruntime/qv4vme_moth.cpp | 6 ++++++ src/qml/qml.pro | 3 +++ src/qml/qml/qqmlobjectcreator.cpp | 20 +++++++++++++------- src/qml/qtqml.tracepoints | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/qml/qtqml.tracepoints (limited to 'src') diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index ea2217499f..c69bb67061 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -63,6 +63,8 @@ #include +#include + #undef COUNT_INSTRUCTIONS extern "C" { @@ -418,6 +420,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) CHECK_STACK_LIMITS(engine); Function *function = frame->v4Function; + Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), + function->compilationUnit->fileName(), + function->compiledFunction->location.line, + function->compiledFunction->location.column); Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); diff --git a/src/qml/qml.pro b/src/qml/qml.pro index db59140f06..d2170ac40b 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -4,6 +4,9 @@ QT = core-private qtConfig(qml-network): \ QT += network +TRACEPOINT_PROVIDER = $$PWD/qtqml.tracepoints +CONFIG += qt_tracepoints + DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x66000000 diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b62b07e39c..d5b15a7a5a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -57,6 +57,8 @@ #include #include +#include + QT_USE_NAMESPACE namespace { @@ -1130,6 +1132,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo { const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index); QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj); + Q_TRACE(QQmlObjectCreator_createInstance_entry, compilationUnit.data(), obj, context->url()); + QString typeName; + Q_TRACE_EXIT(QQmlObjectCreator_createInstance_exit, typeName); ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); @@ -1143,8 +1148,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (obj->flags & QV4::CompiledData::Object::IsComponent) { isComponent = true; QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent); - Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit.data(), obj, QStringLiteral(""), context->url())); + typeName = QStringLiteral(""); QQmlComponentPrivate::get(component)->creationContext = context; instance = component; ddata = QQmlData::get(instance, /*create*/true); @@ -1155,8 +1159,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo installPropertyCache = !typeRef->isFullyDynamicType; QQmlType type = typeRef->type; if (type.isValid()) { - Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit.data(), obj, type.qmlTypeName(), context->url())); + typeName = type.qmlTypeName(); void *ddataMemory = nullptr; type.create(&instance, &ddataMemory, sizeof(QQmlData)); @@ -1188,9 +1191,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo sharedState->allCreatedObjects.push(instance); } else { Q_ASSERT(typeRef->compilationUnit); - Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit.data(), obj, typeRef->compilationUnit->fileName(), - context->url())); + typeName = typeRef->compilationUnit->fileName(); if (typeRef->compilationUnit->unitData()->isSingleton()) { recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); @@ -1217,6 +1218,11 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo ddata = QQmlData::get(instance, /*create*/true); } + + Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( + compilationUnit.data(), obj, typeName, context->url())); + Q_UNUSED(typeName); // only relevant for tracing + ddata->lineNumber = obj->location.line; ddata->columnNumber = obj->location.column; diff --git a/src/qml/qtqml.tracepoints b/src/qml/qtqml.tracepoints new file mode 100644 index 0000000000..841748f201 --- /dev/null +++ b/src/qml/qtqml.tracepoints @@ -0,0 +1,14 @@ +{ +namespace QV4 { +class ExecutionEngine; +namespace CompiledData { +class CompilationUnit; +class Object; +} // CompiledData +} // QV4 +} + +QQmlObjectCreator_createInstance_entry(const QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url) +QQmlObjectCreator_createInstance_exit(const QString &typeName) +QQmlV4_function_call_entry(const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column) +QQmlV4_function_call_exit() -- cgit v1.2.3 From 5032ff5c2b48d53e4580bb7d50b1f6de081263b0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 4 Apr 2019 15:41:18 +0200 Subject: Move workerscript to its own module Change-Id: I778cfe842ddf1c600a837d8f2061a338887eed95 Reviewed-by: Lars Knoll --- src/imports/imports.pro | 2 + src/imports/qtquick2/plugin.cpp | 6 + src/imports/qtquick2/qtquick2.pro | 2 + src/imports/workerscript/dependencies.json | 2 + src/imports/workerscript/plugin.cpp | 81 +++ src/imports/workerscript/qmldir | 3 + src/imports/workerscript/workerscript.pro | 11 + src/qml/jsruntime/jsruntime.pri | 2 - src/qml/jsruntime/qv4objectproto_p.h | 2 +- src/qml/jsruntime/qv4regexpobject.cpp | 7 - src/qml/jsruntime/qv4regexpobject_p.h | 9 +- src/qml/jsruntime/qv4sequenceobject_p.h | 2 +- src/qml/jsruntime/qv4serialize.cpp | 447 ---------------- src/qml/jsruntime/qv4serialize_p.h | 76 --- src/qml/qml/qqmlengine.cpp | 16 - src/qml/qml/qqmlengine_p.h | 4 +- src/qml/types/qquickworkerscript.cpp | 669 ----------------------- src/qml/types/qquickworkerscript_p.h | 123 ----- src/qml/types/types.pri | 7 - src/qmlworkerscript/qmlworkerscript.pro | 20 + src/qmlworkerscript/qqmlworkerscriptmodule.cpp | 62 +++ src/qmlworkerscript/qqmlworkerscriptmodule_p.h | 69 +++ src/qmlworkerscript/qquickworkerscript.cpp | 673 ++++++++++++++++++++++++ src/qmlworkerscript/qquickworkerscript_p.h | 123 +++++ src/qmlworkerscript/qtqmlworkerscriptglobal.h | 58 ++ src/qmlworkerscript/qtqmlworkerscriptglobal_p.h | 60 +++ src/qmlworkerscript/qv4serialize.cpp | 447 ++++++++++++++++ src/qmlworkerscript/qv4serialize_p.h | 76 +++ src/src.pro | 2 + 29 files changed, 1707 insertions(+), 1354 deletions(-) create mode 100644 src/imports/workerscript/dependencies.json create mode 100644 src/imports/workerscript/plugin.cpp create mode 100644 src/imports/workerscript/qmldir create mode 100644 src/imports/workerscript/workerscript.pro delete mode 100644 src/qml/jsruntime/qv4serialize.cpp delete mode 100644 src/qml/jsruntime/qv4serialize_p.h delete mode 100644 src/qml/types/qquickworkerscript.cpp delete mode 100644 src/qml/types/qquickworkerscript_p.h create mode 100644 src/qmlworkerscript/qmlworkerscript.pro create mode 100644 src/qmlworkerscript/qqmlworkerscriptmodule.cpp create mode 100644 src/qmlworkerscript/qqmlworkerscriptmodule_p.h create mode 100644 src/qmlworkerscript/qquickworkerscript.cpp create mode 100644 src/qmlworkerscript/qquickworkerscript_p.h create mode 100644 src/qmlworkerscript/qtqmlworkerscriptglobal.h create mode 100644 src/qmlworkerscript/qtqmlworkerscriptglobal_p.h create mode 100644 src/qmlworkerscript/qv4serialize.cpp create mode 100644 src/qmlworkerscript/qv4serialize_p.h (limited to 'src') diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 901c263be5..6b31dfc65d 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs +QT_FOR_CONFIG += qml-private SUBDIRS += \ builtins \ @@ -6,6 +7,7 @@ SUBDIRS += \ models \ labsmodels +qtConfig(qml-worker-script): SUBDIRS += workerscript qtConfig(thread): SUBDIRS += folderlistmodel qtHaveModule(sql): SUBDIRS += localstorage qtConfig(settings): SUBDIRS += settings diff --git a/src/imports/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp index a5a2c73ced..efa05c349c 100644 --- a/src/imports/qtquick2/plugin.cpp +++ b/src/imports/qtquick2/plugin.cpp @@ -42,6 +42,9 @@ #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #include +#if QT_CONFIG(qml_worker_script) +#include +#endif #endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include @@ -63,6 +66,9 @@ public: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QQmlEnginePrivate::registerQuickTypes(); QQmlModelsModule::registerQuickTypes(); +#if QT_CONFIG(qml_worker_script) + QQmlWorkerScriptModule::registerQuickTypes(); +#endif #endif QQmlQtQuick2Module::defineModule(); diff --git a/src/imports/qtquick2/qtquick2.pro b/src/imports/qtquick2/qtquick2.pro index 1b45d69eb7..8543049ead 100644 --- a/src/imports/qtquick2/qtquick2.pro +++ b/src/imports/qtquick2/qtquick2.pro @@ -8,4 +8,6 @@ SOURCES += \ QT += quick-private qml-private qmlmodels-private +qtConfig(qml-worker-script): QT += qmlworkerscript-private + load(qml_plugin) diff --git a/src/imports/workerscript/dependencies.json b/src/imports/workerscript/dependencies.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/src/imports/workerscript/dependencies.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/imports/workerscript/plugin.cpp b/src/imports/workerscript/plugin.cpp new file mode 100644 index 0000000000..5b3bff7934 --- /dev/null +++ b/src/imports/workerscript/plugin.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQml.WorkerScript 2.\QtMinorVersion + \title Qt QML WorkerScript QML Types + \ingroup qmlmodules + \brief Provides QML types for worker scripts + \since 5.14 + + This QML module contains types for using worker scripts. + + To use the types in this module, import the module with the following line: + + \qml \QtMinorVersion + import QtQml.WorkerScript 2.\1 + \endqml +*/ + +class QtQmlWorkerScriptPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + QtQmlWorkerScriptPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { } + void registerTypes(const char *uri) override + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.WorkerScript")); + + QQmlWorkerScriptModule::defineModule(); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/workerscript/qmldir b/src/imports/workerscript/qmldir new file mode 100644 index 0000000000..0e811d1dbc --- /dev/null +++ b/src/imports/workerscript/qmldir @@ -0,0 +1,3 @@ +module QtQml.WorkerScript +plugin workerscriptsplugin +classname QtQmlWorkerScriptPlugin diff --git a/src/imports/workerscript/workerscript.pro b/src/imports/workerscript/workerscript.pro new file mode 100644 index 0000000000..d48e285bda --- /dev/null +++ b/src/imports/workerscript/workerscript.pro @@ -0,0 +1,11 @@ +CXX_MODULE = qml +TARGET = workerscriptplugin +TARGETPATH = QtQml/WorkerScript.2 +IMPORT_VERSION = 2.$$QT_MINOR_VERSION + +SOURCES += \ + plugin.cpp + +QT = qml-private qmlworkerscript-private + +load(qml_plugin) diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a24ee0a188..ae9a36ca22 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -42,7 +42,6 @@ SOURCES += \ $$PWD/qv4objectiterator.cpp \ $$PWD/qv4regexp.cpp \ $$PWD/qv4runtimecodegen.cpp \ - $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ $$PWD/qv4symbol.cpp \ $$PWD/qv4setobject.cpp \ @@ -109,7 +108,6 @@ HEADERS += \ $$PWD/qv4property_p.h \ $$PWD/qv4objectiterator_p.h \ $$PWD/qv4regexp_p.h \ - $$PWD/qv4serialize_p.h \ $$PWD/qv4script_p.h \ $$PWD/qv4symbol_p.h \ $$PWD/qv4setobject_p.h \ diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index e9515b5b68..8707305dc2 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -74,7 +74,7 @@ struct ObjectCtor: FunctionObject static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; -struct ObjectPrototype: Object +struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 5bd25dcbec..64aba1d85c 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -198,13 +198,6 @@ QString RegExpObject::toString() const return p; } -QString RegExpObject::source() const -{ - Scope scope(engine()); - ScopedValue s(scope, get(scope.engine->id_source())); - return s->toQString(); -} - ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str) { QString s = str->toQString(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 04b533e49d..b94889e9f0 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -101,7 +101,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { } -struct RegExpObject: Object { +struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) V4_INTERNALCLASS(RegExpObject) @@ -145,7 +145,12 @@ struct RegExpObject: Object { QRegularExpression toQRegularExpression() const; #endif QString toString() const; - QString source() const; + QString source() const + { + Scope scope(engine()); + ScopedValue s(scope, get(scope.engine->id_source())); + return s->toQString(); + } Heap::RegExp *value() const { return d()->value; } uint flags() const { return d()->value->flags; } diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index da71215bed..886dfaa521 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct SequencePrototype : public QV4::Object +struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object { V4_PROTOTYPE(arrayPrototype) void init(); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp deleted file mode 100644 index a5e62d3e35..0000000000 --- a/src/qml/jsruntime/qv4serialize.cpp +++ /dev/null @@ -1,447 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4serialize_p.h" - -#include -#include -#include -#if QT_CONFIG(qml_sequence_object) -#include -#endif -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -// We allow the following JavaScript types to be passed between the main and -// the secondary thread: -// + undefined -// + null -// + Boolean -// + String -// + Function -// + Array -// + "Simple" Objects -// + Number -// + Date -// + RegExp -// - -enum Type { - WorkerUndefined, - WorkerNull, - WorkerTrue, - WorkerFalse, - WorkerString, - WorkerFunction, - WorkerArray, - WorkerObject, - WorkerInt32, - WorkerUint32, - WorkerNumber, - WorkerDate, - WorkerRegexp, - WorkerListModel, -#if QT_CONFIG(qml_sequence_object) - WorkerSequence -#endif -}; - -static inline quint32 valueheader(Type type, quint32 size = 0) -{ - return quint8(type) << 24 | (size & 0xFFFFFF); -} - -static inline Type headertype(quint32 header) -{ - return (Type)(header >> 24); -} - -static inline quint32 headersize(quint32 header) -{ - return header & 0xFFFFFF; -} - -static inline void push(QByteArray &data, quint32 value) -{ - data.append((const char *)&value, sizeof(quint32)); -} - -static inline void push(QByteArray &data, double value) -{ - data.append((const char *)&value, sizeof(double)); -} - -static inline void push(QByteArray &data, void *ptr) -{ - data.append((const char *)&ptr, sizeof(void *)); -} - -static inline void reserve(QByteArray &data, int extra) -{ - data.reserve(data.size() + extra); -} - -static inline quint32 popUint32(const char *&data) -{ - quint32 rv = *((const quint32 *)data); - data += sizeof(quint32); - return rv; -} - -static inline double popDouble(const char *&data) -{ - double rv = *((const double *)data); - data += sizeof(double); - return rv; -} - -static inline void *popPtr(const char *&data) -{ - void *rv = *((void *const *)data); - data += sizeof(void *); - return rv; -} - -// XXX TODO: Check that worker script is exception safe in the case of -// serialization/deserialization failures - -#define ALIGN(size) (((size) + 3) & ~3) -void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine) -{ - QV4::Scope scope(engine); - - if (v.isEmpty()) { - Q_ASSERT(!"Serialize: got empty value"); - } else if (v.isUndefined()) { - push(data, valueheader(WorkerUndefined)); - } else if (v.isNull()) { - push(data, valueheader(WorkerNull)); - } else if (v.isBoolean()) { - push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); - } else if (v.isString()) { - const QString &qstr = v.toQString(); - int length = qstr.length(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, utf16size + sizeof(quint32)); - push(data, valueheader(WorkerString, length)); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.as()) { - // XXX TODO: Implement passing function objects between the main and - // worker scripts - push(data, valueheader(WorkerUndefined)); - } else if (const QV4::ArrayObject *array = v.as()) { - uint length = array->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerArray, length)); - ScopedValue val(scope); - for (uint ii = 0; ii < length; ++ii) - serialize(data, (val = array->get(ii)), engine); - } else if (v.isInteger()) { - reserve(data, 2 * sizeof(quint32)); - push(data, valueheader(WorkerInt32)); - push(data, (quint32)v.integerValue()); -// } else if (v.IsUint32()) { -// reserve(data, 2 * sizeof(quint32)); -// push(data, valueheader(WorkerUint32)); -// push(data, v.Uint32Value()); - } else if (v.isNumber()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerNumber)); - push(data, v.asDouble()); - } else if (const QV4::DateObject *d = v.as()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerDate)); - push(data, d->date()); - } else if (const RegExpObject *re = v.as()) { - quint32 flags = re->flags(); - QString pattern = re->source(); - int length = pattern.length() + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, sizeof(quint32) + utf16size); - push(data, valueheader(WorkerRegexp, flags)); - push(data, (quint32)length); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (const QObjectWrapper *qobjectWrapper = v.as()) { - // XXX TODO: Generalize passing objects between the main thread and worker scripts so - // that others can trivially plug in their elements. - if (QObject *lm = qobjectWrapper->object()) { - if (QObject *agent = qvariant_cast(lm->property("agent"))) { - if (QMetaObject::invokeMethod(agent, "addref")) { - push(data, valueheader(WorkerListModel)); - push(data, (void *)agent); - return; - } - } - } - // No other QObject's are allowed to be sent - push(data, valueheader(WorkerUndefined)); - } else if (const Object *o = v.as()) { -#if QT_CONFIG(qml_sequence_object) - if (o->isListType()) { - // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); - uint length = seqLength + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerSequence, length)); - serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type - ScopedValue val(scope); - for (uint ii = 0; ii < seqLength; ++ii) - serialize(data, (val = o->get(ii)), engine); // sequence elements - - return; - } -#endif - - // regular object - QV4::ScopedValue val(scope, v); - QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val)); - quint32 length = properties->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - push(data, valueheader(WorkerObject, length)); - - QV4::ScopedValue s(scope); - for (quint32 ii = 0; ii < length; ++ii) { - s = properties->get(ii); - serialize(data, s, engine); - - QV4::String *str = s->as(); - val = o->get(str); - if (scope.hasException()) - scope.engine->catchException(); - - serialize(data, val, engine); - } - return; - } else { - push(data, valueheader(WorkerUndefined)); - } -} - -struct VariantRef -{ - VariantRef() : obj(nullptr) {} - VariantRef(const VariantRef &r) : obj(r.obj) { addref(); } - VariantRef(QObject *a) : obj(a) { addref(); } - ~VariantRef() { release(); } - - VariantRef &operator=(const VariantRef &o) { - o.addref(); - release(); - obj = o.obj; - return *this; - } - - void addref() const - { - if (obj) - QMetaObject::invokeMethod(obj, "addref"); - } - - void release() const - { - if (obj) - QMetaObject::invokeMethod(obj, "release"); - - } - - QObject *obj; -}; - -QT_END_NAMESPACE -Q_DECLARE_METATYPE(VariantRef) -Q_DECLARE_METATYPE(QV4::ExecutionEngine *) -QT_BEGIN_NAMESPACE - -ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) -{ - quint32 header = popUint32(data); - Type type = headertype(header); - - Scope scope(engine); - - switch (type) { - case WorkerUndefined: - return QV4::Encode::undefined(); - case WorkerNull: - return QV4::Encode::null(); - case WorkerTrue: - return QV4::Encode(true); - case WorkerFalse: - return QV4::Encode(false); - case WorkerString: - { - quint32 size = headersize(header); - QString qstr((const QChar *)data, size); - data += ALIGN(size * sizeof(quint16)); - return QV4::Encode(engine->newString(qstr)); - } - case WorkerFunction: - Q_ASSERT(!"Unreachable"); - break; - case WorkerArray: - { - quint32 size = headersize(header); - ScopedArrayObject a(scope, engine->newArrayObject()); - ScopedValue v(scope); - for (quint32 ii = 0; ii < size; ++ii) { - v = deserialize(data, engine); - a->put(ii, v); - } - return a.asReturnedValue(); - } - case WorkerObject: - { - quint32 size = headersize(header); - ScopedObject o(scope, engine->newObject()); - ScopedValue name(scope); - ScopedString n(scope); - ScopedValue value(scope); - for (quint32 ii = 0; ii < size; ++ii) { - name = deserialize(data, engine); - value = deserialize(data, engine); - n = name->asReturnedValue(); - o->put(n, value); - } - return o.asReturnedValue(); - } - case WorkerInt32: - return QV4::Encode((qint32)popUint32(data)); - case WorkerUint32: - return QV4::Encode(popUint32(data)); - case WorkerNumber: - return QV4::Encode(popDouble(data)); - case WorkerDate: - return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data)))); - case WorkerRegexp: - { - quint32 flags = headersize(header); - quint32 length = popUint32(data); - QString pattern = QString((const QChar *)data, length - 1); - data += ALIGN(length * sizeof(quint16)); - return Encode(engine->newRegExpObject(pattern, flags)); - } - case WorkerListModel: - { - QObject *agent = reinterpret_cast(popPtr(data)); - QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent)); - // ### Find a better solution then the ugly property - VariantRef ref(agent); - QVariant var = QVariant::fromValue(ref); - QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); - QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); - rv->as()->defineReadonlyProperty(s, v); - - QMetaObject::invokeMethod(agent, "release"); - agent->setProperty("engine", QVariant::fromValue(engine)); - return rv->asReturnedValue(); - } -#if QT_CONFIG(qml_sequence_object) - case WorkerSequence: - { - ScopedValue value(scope); - bool succeeded = false; - quint32 length = headersize(header); - quint32 seqLength = length - 1; - value = deserialize(data, engine); - int sequenceType = value->integerValue(); - ScopedArrayObject array(scope, engine->newArrayObject()); - array->arrayReserve(seqLength); - for (quint32 ii = 0; ii < seqLength; ++ii) { - value = deserialize(data, engine); - array->arrayPut(ii, value); - } - array->setArrayLengthUnchecked(seqLength); - QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); - return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); - } -#endif - } - Q_ASSERT(!"Unreachable"); - return QV4::Encode::undefined(); -} - -QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine) -{ - QByteArray rv; - serialize(rv, value, engine); - return rv; -} - -ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine) -{ - const char *stream = data.constData(); - return deserialize(stream, engine); -} - -QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h deleted file mode 100644 index c8700c3ca5..0000000000 --- a/src/qml/jsruntime/qv4serialize_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SERIALIZE_P_H -#define QV4SERIALIZE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -class Serialize { -public: - - static QByteArray serialize(const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const QByteArray &, ExecutionEngine *); - -private: - static void serialize(QByteArray &, const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const char *&, ExecutionEngine *); -}; - -} - -QT_END_NAMESPACE - -#endif // QV8WORKER_P_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index bb2b3e462c..cb94ae379e 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -87,9 +87,6 @@ #include #endif #include -#if QT_CONFIG(qml_worker_script) -#include -#endif #include #ifdef Q_OS_WIN // for %APPDATA% @@ -239,9 +236,6 @@ void QQmlEnginePrivate::registerQuickTypes() #endif qmlRegisterType(uri, 2, 8, "LoggingCategory"); qmlRegisterType(uri, 2, 12, "LoggingCategory"); -#if QT_CONFIG(qml_worker_script) - qmlRegisterType(uri, 2, 0, "WorkerScript"); -#endif #if QT_CONFIG(qml_locale) qmlRegisterUncreatableType(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); #endif @@ -968,16 +962,6 @@ void QQmlEnginePrivate::init() rootContext = new QQmlContext(q,true); } -#if QT_CONFIG(qml_worker_script) -QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() -{ - Q_Q(QQmlEngine); - if (!workerScriptEngine) - workerScriptEngine = new QQuickWorkerScriptEngine(q); - return workerScriptEngine; -} -#endif - /*! \class QQmlEngine \since 5.0 diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 4f7fb79593..6ef88404fd 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -94,7 +94,6 @@ class QQmlTypeNameCache; class QQmlComponentAttached; class QQmlCleanup; class QQmlDelayedError; -class QQuickWorkerScriptEngine; class QQmlObjectCreator; class QDir; class QQmlIncubator; @@ -154,8 +153,7 @@ public: QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); } #if QT_CONFIG(qml_worker_script) - QQuickWorkerScriptEngine *getWorkerScriptEngine(); - QQuickWorkerScriptEngine *workerScriptEngine; + QThread *workerScriptEngine; #endif QUrl baseUrl; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp deleted file mode 100644 index c081c9e7fc..0000000000 --- a/src/qml/types/qquickworkerscript.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtqmlglobal_p.h" -#include "qquickworkerscript_p.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if QT_CONFIG(qml_network) -#include -#include "qqmlnetworkaccessmanagerfactory.h" -#endif - -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray m_data; -}; - -class WorkerLoadEvent : public QEvent -{ -public: - enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; - - WorkerLoadEvent(int workerId, const QUrl &url); - - int workerId() const; - QUrl url() const; - -private: - int m_id; - QUrl m_url; -}; - -class WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript : public QV8Engine { - WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent); - ~WorkerScript() override; - -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() override; -#endif - - QQuickWorkerScriptEnginePrivate *p = nullptr; - QUrl source; - QQuickWorkerScript *owner = nullptr; -#if QT_CONFIG(qml_network) - QScopedPointer accessManager; -#endif - int id = -1; - }; - - QHash workers; - QV4::ReturnedValue getWorker(WorkerScript *); - - int m_nextId; - - static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - -signals: - void stopThread(); - -protected: - bool event(QEvent *) override; - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: qmlengine(engine), m_nextId(0) -{ -} - -QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b, - const QV4::Value *, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - WorkerScript *script = static_cast(scope.engine->v8Engine); - - QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue()); - QByteArray data = QV4::Serialize::serialize(v, scope.engine); - - QMutexLocker locker(&script->p->m_lock); - if (script && script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return QV4::Encode::undefined(); -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - QMutexLocker locker(&m_lock); - WorkerRemoveEvent *workerEvent = static_cast(event); - QHash::iterator itr = workers.find(workerEvent->workerId()); - if (itr != workers.end()) { - delete itr.value(); - workers.erase(itr); - } - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - QV4::Scope scope(v4); - QV4::ScopedString v(scope); - QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript"))))); - QV4::ScopedFunctionObject onmessage(scope); - if (worker) - onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage")))); - - if (!onmessage) - return; - - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); - - QV4::JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = v4->global(); - jsCallData->args[0] = value; - onmessage->call(jsCallData); - if (scope.hasException()) { - QQmlError error = scope.engine->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - - script->source = url; - - if (fileName.endsWith(QLatin1String(".mjs"))) { - auto moduleUnit = v4->loadModule(url); - if (moduleUnit) { - if (moduleUnit->instantiate(v4)) - moduleUnit->evaluate(); - } else { - v4->throwError(QStringLiteral("Could not load module file")); - } - } else { - QString error; - QV4::Scope scope(v4); - QScopedPointer program; - program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error)); - if (program.isNull()) { - if (!error.isEmpty()) - qWarning().nospace() << error; - return; - } - - if (!v4->hasException) - program->run(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QMutexLocker locker(&script->p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) -: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) -{ -} - -int WorkerLoadEvent::workerId() const -{ - return m_id; -} - -QUrl WorkerLoadEvent::url() const -{ - return m_url; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(new QV4::ExecutionEngine) - , p(parent) - , id(id) -{ - m_v4Engine->v8Engine = this; - - initQmlGlobalObject(); - - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject api(scope, scope.engine->newObject()); - QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); - api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage); - m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - delete m_v4Engine; -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p)); - } else { - accessManager.reset(new QNetworkAccessManager(p)); - } - } - return accessManager.data(); -} -#endif - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript(d->m_nextId++, d); - - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = nullptr; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQml - \brief Enables the use of threads in a Qt Quick application. - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \c onMessage() handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.mjs", that handles - the operations to be performed in the new thread. Here is \c script.mjs: - - \quotefile qml/workerscript/script.mjs - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.mjs. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - The example uses a script that is an ECMAScript module, because it has the ".mjs" extension. - It can use import statements to access functionality from other modules and it is run in JavaScript - strict mode. - - If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript - statements and it is run in non-strict mode. - - \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect - isolation and thread-safety. If the impact of that results in a memory consumption that is too - high for your environment, then consider sharing a WorkerScript element. - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax. - Scripts that are ECMAScript modules can freely use import and export statements. - - \sa {Qt Quick Examples - Threading}, - {Threaded ListModel Example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. - - If the file name component of the url ends with ".mjs", then the script - is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be - plain script. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV4Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue argument(scope, QV4::Value::undefinedValue()); - if (args->length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine)); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return nullptr; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return nullptr; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::message(jsobject msg) - - This signal is emitted when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). - - The corresponding handler is \c onMessage. -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - if (QQmlEngine *engine = qmlEngine(this)) { - QV4::ExecutionEngine *v4 = engine->handle(); - WorkerDataEvent *workerEvent = static_cast(event); - emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4))); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include - -#include "moc_qquickworkerscript_p.cpp" diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h deleted file mode 100644 index 87cf2e9754..0000000000 --- a/src/qml/types/qquickworkerscript_p.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread -{ -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr); - ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - void run() override; - -private: - QQuickWorkerScriptEnginePrivate *d; -}; - -class QQmlV4Function; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - - Q_INTERFACES(QQmlParserStatus) -public: - QQuickWorkerScript(QObject *parent = nullptr); - ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); - -public Q_SLOTS: - void sendMessage(QQmlV4Function*); - -Q_SIGNALS: - void sourceChanged(); - void message(const QJSValue &messageObject); - -protected: - void classBegin() override; - void componentComplete() override; - bool event(QEvent *) override; - -private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index ba11271d66..c50273071b 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -8,13 +8,6 @@ HEADERS += \ $$PWD/qqmlconnections_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h -qtConfig(qml-worker-script) { - SOURCES += \ - $$PWD/qquickworkerscript.cpp - HEADERS += \ - $$PWD/qquickworkerscript_p.h -} - qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp diff --git a/src/qmlworkerscript/qmlworkerscript.pro b/src/qmlworkerscript/qmlworkerscript.pro new file mode 100644 index 0000000000..908caa4ed4 --- /dev/null +++ b/src/qmlworkerscript/qmlworkerscript.pro @@ -0,0 +1,20 @@ +TARGET = QtQmlWorkerScript +QT = core-private qml-private + +DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH + +HEADERS += \ + qqmlworkerscriptmodule_p.h \ + qquickworkerscript_p.h \ + qtqmlworkerscriptglobal.h \ + qtqmlworkerscriptglobal_p.h \ + qv4serialize_p.h + +SOURCES += \ + qqmlworkerscriptmodule.cpp \ + qquickworkerscript.cpp \ + qv4serialize.cpp + +include(../3rdparty/masm/masm-defs.pri) + +load(qt_module) diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule.cpp b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp new file mode 100644 index 0000000000..98e82dbeba --- /dev/null +++ b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlworkerscriptmodule_p.h" +#include "qquickworkerscript_p.h" + +QT_BEGIN_NAMESPACE + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +void QQmlWorkerScriptModule::registerQuickTypes() +{ + // Don't add anything here. These are only for backwards compatibility. + const char uri[] = "QtQuick"; + qmlRegisterType(uri, 2, 0, "WorkerScript"); +} + +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +void QQmlWorkerScriptModule::defineModule() +{ + const char uri[] = "QtQml.WorkerScript"; + qmlRegisterType(uri, 2, 0, "WorkerScript"); +} + +QT_END_NAMESPACE diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule_p.h b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h new file mode 100644 index 0000000000..a2efb304c1 --- /dev/null +++ b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLWORKERSCRIPTMODULE_P_H +#define QQMLWORKERSCRIPTMODULE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class Q_QMLWORKERSCRIPT_PRIVATE_EXPORT QQmlWorkerScriptModule +{ +public: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + static void registerQuickTypes(); +#endif + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif // QQMLWORKERSCRIPTMODULE_P_H diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp new file mode 100644 index 0000000000..273ec2a7a6 --- /dev/null +++ b/src/qmlworkerscript/qquickworkerscript.cpp @@ -0,0 +1,673 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtqmlworkerscriptglobal_p.h" +#include "qquickworkerscript_p.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_CONFIG(qml_network) +#include +#include "qqmlnetworkaccessmanagerfactory.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class WorkerDataEvent : public QEvent +{ +public: + enum Type { WorkerData = QEvent::User }; + + WorkerDataEvent(int workerId, const QByteArray &data); + virtual ~WorkerDataEvent(); + + int workerId() const; + QByteArray data() const; + +private: + int m_id; + QByteArray m_data; +}; + +class WorkerLoadEvent : public QEvent +{ +public: + enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; + + WorkerLoadEvent(int workerId, const QUrl &url); + + int workerId() const; + QUrl url() const; + +private: + int m_id; + QUrl m_url; +}; + +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + +class WorkerErrorEvent : public QEvent +{ +public: + enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; + + WorkerErrorEvent(const QQmlError &error); + + QQmlError error() const; + +private: + QQmlError m_error; +}; + +class QQuickWorkerScriptEnginePrivate : public QObject +{ + Q_OBJECT +public: + enum WorkerEventTypes { + WorkerDestroyEvent = QEvent::User + 100 + }; + + QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); + + QQmlEngine *qmlengine; + + QMutex m_lock; + QWaitCondition m_wait; + + struct WorkerScript : public QV8Engine { + WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent); + ~WorkerScript() override; + +#if QT_CONFIG(qml_network) + QNetworkAccessManager *networkAccessManager() override; +#endif + + QQuickWorkerScriptEnginePrivate *p = nullptr; + QUrl source; + QQuickWorkerScript *owner = nullptr; +#if QT_CONFIG(qml_network) + QScopedPointer accessManager; +#endif + int id = -1; + }; + + QHash workers; + QV4::ReturnedValue getWorker(WorkerScript *); + + int m_nextId; + + static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); + +signals: + void stopThread(); + +protected: + bool event(QEvent *) override; + +private: + void processMessage(int, const QByteArray &); + void processLoad(int, const QUrl &); + void reportScriptException(WorkerScript *, const QQmlError &error); +}; + +QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) +: qmlengine(engine), m_nextId(0) +{ +} + +QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b, + const QV4::Value *, const QV4::Value *argv, int argc) +{ + QV4::Scope scope(b); + WorkerScript *script = static_cast(scope.engine->v8Engine); + + QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue()); + QByteArray data = QV4::Serialize::serialize(v, scope.engine); + + QMutexLocker locker(&script->p->m_lock); + if (script && script->owner) + QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); + + return QV4::Encode::undefined(); +} + +bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + WorkerDataEvent *workerEvent = static_cast(event); + processMessage(workerEvent->workerId(), workerEvent->data()); + return true; + } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { + WorkerLoadEvent *workerEvent = static_cast(event); + processLoad(workerEvent->workerId(), workerEvent->url()); + return true; + } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { + emit stopThread(); + return true; + } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { + QMutexLocker locker(&m_lock); + WorkerRemoveEvent *workerEvent = static_cast(event); + QHash::iterator itr = workers.find(workerEvent->workerId()); + if (itr != workers.end()) { + delete itr.value(); + workers.erase(itr); + } + return true; + } else { + return QObject::event(event); + } +} + +void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) +{ + WorkerScript *script = workers.value(id); + if (!script) + return; + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); + QV4::Scope scope(v4); + QV4::ScopedString v(scope); + QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript"))))); + QV4::ScopedFunctionObject onmessage(scope); + if (worker) + onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage")))); + + if (!onmessage) + return; + + QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); + + QV4::JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = v4->global(); + jsCallData->args[0] = value; + onmessage->call(jsCallData); + if (scope.hasException()) { + QQmlError error = scope.engine->catchExceptionAsQmlError(); + reportScriptException(script, error); + } +} + +void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) +{ + if (url.isRelative()) + return; + + QString fileName = QQmlFile::urlToLocalFileOrQrc(url); + + WorkerScript *script = workers.value(id); + if (!script) + return; + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); + + script->source = url; + + if (fileName.endsWith(QLatin1String(".mjs"))) { + auto moduleUnit = v4->loadModule(url); + if (moduleUnit) { + if (moduleUnit->instantiate(v4)) + moduleUnit->evaluate(); + } else { + v4->throwError(QStringLiteral("Could not load module file")); + } + } else { + QString error; + QV4::Scope scope(v4); + QScopedPointer program; + program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error)); + if (program.isNull()) { + if (!error.isEmpty()) + qWarning().nospace() << error; + return; + } + + if (!v4->hasException) + program->run(); + } + + if (v4->hasException) { + QQmlError error = v4->catchExceptionAsQmlError(); + reportScriptException(script, error); + } +} + +void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, + const QQmlError &error) +{ + QMutexLocker locker(&script->p->m_lock); + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) +: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) +{ +} + +WorkerDataEvent::~WorkerDataEvent() +{ +} + +int WorkerDataEvent::workerId() const +{ + return m_id; +} + +QByteArray WorkerDataEvent::data() const +{ + return m_data; +} + +WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) +: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) +{ +} + +int WorkerLoadEvent::workerId() const +{ + return m_id; +} + +QUrl WorkerLoadEvent::url() const +{ + return m_url; +} + +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + +WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) +: QEvent((QEvent::Type)WorkerError), m_error(error) +{ +} + +QQmlError WorkerErrorEvent::error() const +{ + return m_error; +} + +QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) +: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) +{ + d->m_lock.lock(); + connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); + start(QThread::LowestPriority); + d->m_wait.wait(&d->m_lock); + d->moveToThread(this); + d->m_lock.unlock(); +} + +QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() +{ + d->m_lock.lock(); + QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); + d->m_lock.unlock(); + + //We have to force to cleanup the main thread's event queue here + //to make sure the main GUI release all pending locks/wait conditions which + //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). + while (!isFinished()) { + // We can't simply wait here, because the worker thread will not terminate + // until the main thread processes the last data event it generates + QCoreApplication::processEvents(); + yieldCurrentThread(); + } + + d->deleteLater(); +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent) + : QV8Engine(new QV4::ExecutionEngine) + , p(parent) + , id(id) +{ + m_v4Engine->v8Engine = this; + + initQmlGlobalObject(); + + QV4::Scope scope(m_v4Engine); + QV4::ScopedObject api(scope, scope.engine->newObject()); + QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); + QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); + api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage); + m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() +{ + delete m_v4Engine; +} + +#if QT_CONFIG(qml_network) +QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager() +{ + if (!accessManager) { + if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { + accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p)); + } else { + accessManager.reset(new QNetworkAccessManager(p)); + } + } + return accessManager.data(); +} +#endif + +int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) +{ + typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; + WorkerScript *script = new WorkerScript(d->m_nextId++, d); + + script->owner = owner; + + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; +} + +void QQuickWorkerScriptEngine::removeWorkerScript(int id) +{ + QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); + if (script) { + script->owner = nullptr; + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); + } +} + +void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) +{ + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); +} + +void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) +{ + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); +} + +void QQuickWorkerScriptEngine::run() +{ + d->m_lock.lock(); + + d->m_wait.wakeAll(); + + d->m_lock.unlock(); + + exec(); + + qDeleteAll(d->workers); + d->workers.clear(); +} + + +/*! + \qmltype WorkerScript + \instantiates QQuickWorkerScript + \ingroup qtquick-threading + \inqmlmodule QtQml + \brief Enables the use of threads in a Qt Quick application. + + Use WorkerScript to run operations in a new thread. + This is useful for running operations in the background so + that the main GUI thread is not blocked. + + Messages can be passed between the new thread and the parent thread + using \l sendMessage() and the \c onMessage() handler. + + An example: + + \snippet qml/workerscript/workerscript.qml 0 + + The above worker script specifies a JavaScript file, "script.mjs", that handles + the operations to be performed in the new thread. Here is \c script.mjs: + + \quotefile qml/workerscript/script.mjs + + When the user clicks anywhere within the rectangle, \c sendMessage() is + called, triggering the \tt WorkerScript.onMessage() handler in + \tt script.mjs. This in turn sends a reply message that is then received + by the \tt onMessage() handler of \tt myWorker. + + The example uses a script that is an ECMAScript module, because it has the ".mjs" extension. + It can use import statements to access functionality from other modules and it is run in JavaScript + strict mode. + + If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript + statements and it is run in non-strict mode. + + \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect + isolation and thread-safety. If the impact of that results in a memory consumption that is too + high for your environment, then consider sharing a WorkerScript element. + + \section3 Restrictions + + Since the \c WorkerScript.onMessage() function is run in a separate thread, the + JavaScript file is evaluated in a context separate from the main QML engine. This means + that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs + in the above example cannot access the properties, methods or other attributes + of the QML item, nor can it access any context properties set on the QML object + through QQmlContext. + + Additionally, there are restrictions on the types of values that can be passed to and + from the worker script. See the sendMessage() documentation for details. + + Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax. + Scripts that are ECMAScript modules can freely use import and export statements. + + \sa {Qt Quick Examples - Threading}, + {Threaded ListModel Example} +*/ +QQuickWorkerScript::QQuickWorkerScript(QObject *parent) +: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true) +{ +} + +QQuickWorkerScript::~QQuickWorkerScript() +{ + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); +} + +/*! + \qmlproperty url WorkerScript::source + + This holds the url of the JavaScript file that implements the + \tt WorkerScript.onMessage() handler for threaded operations. + + If the file name component of the url ends with ".mjs", then the script + is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be + plain script. +*/ +QUrl QQuickWorkerScript::source() const +{ + return m_source; +} + +void QQuickWorkerScript::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + m_source = source; + + if (engine()) + m_engine->executeUrl(m_scriptId, m_source); + + emit sourceChanged(); +} + +/*! + \qmlmethod WorkerScript::sendMessage(jsobject message) + + Sends the given \a message to a worker script handler in another + thread. The other worker script handler can receive this message + through the onMessage() handler. + + The \c message object may only contain values of the following + types: + + \list + \li boolean, number, string + \li JavaScript objects and arrays + \li ListModel objects (any other type of QObject* is not allowed) + \endlist + + All objects and arrays are copied to the \c message. With the exception + of ListModel objects, any modifications by the other thread to an object + passed in \c message will not be reflected in the original object. +*/ +void QQuickWorkerScript::sendMessage(QQmlV4Function *args) +{ + if (!engine()) { + qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); + return; + } + + QV4::Scope scope(args->v4engine()); + QV4::ScopedValue argument(scope, QV4::Value::undefinedValue()); + if (args->length() != 0) + argument = (*args)[0]; + + m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine)); +} + +void QQuickWorkerScript::classBegin() +{ + m_componentComplete = false; +} + +QQuickWorkerScriptEngine *QQuickWorkerScript::engine() +{ + if (m_engine) return m_engine; + if (m_componentComplete) { + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); + return nullptr; + } + + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + if (enginePrivate->workerScriptEngine == nullptr) + enginePrivate->workerScriptEngine = new QQuickWorkerScriptEngine(engine); + m_engine = qobject_cast(enginePrivate->workerScriptEngine); + Q_ASSERT(m_engine); + m_scriptId = m_engine->registerWorkerScript(this); + + if (m_source.isValid()) + m_engine->executeUrl(m_scriptId, m_source); + + return m_engine; + } + return nullptr; +} + +void QQuickWorkerScript::componentComplete() +{ + m_componentComplete = true; + engine(); // Get it started now. +} + +/*! + \qmlsignal WorkerScript::message(jsobject msg) + + This signal is emitted when a message \a msg is received from a worker + script in another thread through a call to sendMessage(). + + The corresponding handler is \c onMessage. +*/ + +bool QQuickWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + if (QQmlEngine *engine = qmlEngine(this)) { + QV4::ExecutionEngine *v4 = engine->handle(); + WorkerDataEvent *workerEvent = static_cast(event); + emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4))); + } + return true; + } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { + WorkerErrorEvent *workerEvent = static_cast(event); + QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); + return true; + } else { + return QObject::event(event); + } +} + +QT_END_NAMESPACE + +#include + +#include "moc_qquickworkerscript_p.cpp" diff --git a/src/qmlworkerscript/qquickworkerscript_p.h b/src/qmlworkerscript/qquickworkerscript_p.h new file mode 100644 index 0000000000..87cf2e9754 --- /dev/null +++ b/src/qmlworkerscript/qquickworkerscript_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWORKERSCRIPT_P_H +#define QQUICKWORKERSCRIPT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class QQuickWorkerScript; +class QQuickWorkerScriptEnginePrivate; +class QQuickWorkerScriptEngine : public QThread +{ +Q_OBJECT +public: + QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr); + ~QQuickWorkerScriptEngine(); + + int registerWorkerScript(QQuickWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QByteArray &); + +protected: + void run() override; + +private: + QQuickWorkerScriptEnginePrivate *d; +}; + +class QQmlV4Function; +class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + + Q_INTERFACES(QQmlParserStatus) +public: + QQuickWorkerScript(QObject *parent = nullptr); + ~QQuickWorkerScript(); + + QUrl source() const; + void setSource(const QUrl &); + +public Q_SLOTS: + void sendMessage(QQmlV4Function*); + +Q_SIGNALS: + void sourceChanged(); + void message(const QJSValue &messageObject); + +protected: + void classBegin() override; + void componentComplete() override; + bool event(QEvent *) override; + +private: + QQuickWorkerScriptEngine *engine(); + QQuickWorkerScriptEngine *m_engine; + int m_scriptId; + QUrl m_source; + bool m_componentComplete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickWorkerScript) + +#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal.h b/src/qmlworkerscript/qtqmlworkerscriptglobal.h new file mode 100644 index 0000000000..be3ea4e12a --- /dev/null +++ b/src/qmlworkerscript/qtqmlworkerscriptglobal.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTQMLWORKERSCRIPTGLOBAL_H +#define QTQMLWORKERSCRIPTGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#if !defined(QT_STATIC) +# if defined(QT_BUILD_QMLWORKERSCRIPT_LIB) +# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_EXPORT +# else +# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_QMLWORKERSCRIPT_EXPORT +#endif + +QT_END_NAMESPACE +#endif // QTQMLWORKERSCRIPTGLOBAL_H diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h new file mode 100644 index 0000000000..34236cd79e --- /dev/null +++ b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTQMLWORKERSCRIPTGLOBAL_P_H +#define QTQMLWORKERSCRIPTGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#define Q_QMLWORKERSCRIPT_PRIVATE_EXPORT Q_QMLWORKERSCRIPT_EXPORT +#define Q_QMLWORKERSCRIPT_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT + +#endif // QTQMLWORKERSCRIPTGLOBAL_P_H diff --git a/src/qmlworkerscript/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp new file mode 100644 index 0000000000..a5e62d3e35 --- /dev/null +++ b/src/qmlworkerscript/qv4serialize.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4serialize_p.h" + +#include +#include +#include +#if QT_CONFIG(qml_sequence_object) +#include +#endif +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +// We allow the following JavaScript types to be passed between the main and +// the secondary thread: +// + undefined +// + null +// + Boolean +// + String +// + Function +// + Array +// + "Simple" Objects +// + Number +// + Date +// + RegExp +// + +enum Type { + WorkerUndefined, + WorkerNull, + WorkerTrue, + WorkerFalse, + WorkerString, + WorkerFunction, + WorkerArray, + WorkerObject, + WorkerInt32, + WorkerUint32, + WorkerNumber, + WorkerDate, + WorkerRegexp, + WorkerListModel, +#if QT_CONFIG(qml_sequence_object) + WorkerSequence +#endif +}; + +static inline quint32 valueheader(Type type, quint32 size = 0) +{ + return quint8(type) << 24 | (size & 0xFFFFFF); +} + +static inline Type headertype(quint32 header) +{ + return (Type)(header >> 24); +} + +static inline quint32 headersize(quint32 header) +{ + return header & 0xFFFFFF; +} + +static inline void push(QByteArray &data, quint32 value) +{ + data.append((const char *)&value, sizeof(quint32)); +} + +static inline void push(QByteArray &data, double value) +{ + data.append((const char *)&value, sizeof(double)); +} + +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + +static inline void reserve(QByteArray &data, int extra) +{ + data.reserve(data.size() + extra); +} + +static inline quint32 popUint32(const char *&data) +{ + quint32 rv = *((const quint32 *)data); + data += sizeof(quint32); + return rv; +} + +static inline double popDouble(const char *&data) +{ + double rv = *((const double *)data); + data += sizeof(double); + return rv; +} + +static inline void *popPtr(const char *&data) +{ + void *rv = *((void *const *)data); + data += sizeof(void *); + return rv; +} + +// XXX TODO: Check that worker script is exception safe in the case of +// serialization/deserialization failures + +#define ALIGN(size) (((size) + 3) & ~3) +void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine) +{ + QV4::Scope scope(engine); + + if (v.isEmpty()) { + Q_ASSERT(!"Serialize: got empty value"); + } else if (v.isUndefined()) { + push(data, valueheader(WorkerUndefined)); + } else if (v.isNull()) { + push(data, valueheader(WorkerNull)); + } else if (v.isBoolean()) { + push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); + } else if (v.isString()) { + const QString &qstr = v.toQString(); + int length = qstr.length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(quint16)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(WorkerString, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, qstr.constData(), length*sizeof(QChar)); + } else if (v.as()) { + // XXX TODO: Implement passing function objects between the main and + // worker scripts + push(data, valueheader(WorkerUndefined)); + } else if (const QV4::ArrayObject *array = v.as()) { + uint length = array->getLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerArray, length)); + ScopedValue val(scope); + for (uint ii = 0; ii < length; ++ii) + serialize(data, (val = array->get(ii)), engine); + } else if (v.isInteger()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerInt32)); + push(data, (quint32)v.integerValue()); +// } else if (v.IsUint32()) { +// reserve(data, 2 * sizeof(quint32)); +// push(data, valueheader(WorkerUint32)); +// push(data, v.Uint32Value()); + } else if (v.isNumber()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerNumber)); + push(data, v.asDouble()); + } else if (const QV4::DateObject *d = v.as()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerDate)); + push(data, d->date()); + } else if (const RegExpObject *re = v.as()) { + quint32 flags = re->flags(); + QString pattern = re->source(); + int length = pattern.length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(quint16)); + + reserve(data, sizeof(quint32) + utf16size); + push(data, valueheader(WorkerRegexp, flags)); + push(data, (quint32)length); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, pattern.constData(), length*sizeof(QChar)); + } else if (const QObjectWrapper *qobjectWrapper = v.as()) { + // XXX TODO: Generalize passing objects between the main thread and worker scripts so + // that others can trivially plug in their elements. + if (QObject *lm = qobjectWrapper->object()) { + if (QObject *agent = qvariant_cast(lm->property("agent"))) { + if (QMetaObject::invokeMethod(agent, "addref")) { + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + } + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); + } else if (const Object *o = v.as()) { +#if QT_CONFIG(qml_sequence_object) + if (o->isListType()) { + // valid sequence. we generate a length (sequence length + 1 for the sequence type) + uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); + uint length = seqLength + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerSequence, length)); + serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type + ScopedValue val(scope); + for (uint ii = 0; ii < seqLength; ++ii) + serialize(data, (val = o->get(ii)), engine); // sequence elements + + return; + } +#endif + + // regular object + QV4::ScopedValue val(scope, v); + QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val)); + quint32 length = properties->getLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + push(data, valueheader(WorkerObject, length)); + + QV4::ScopedValue s(scope); + for (quint32 ii = 0; ii < length; ++ii) { + s = properties->get(ii); + serialize(data, s, engine); + + QV4::String *str = s->as(); + val = o->get(str); + if (scope.hasException()) + scope.engine->catchException(); + + serialize(data, val, engine); + } + return; + } else { + push(data, valueheader(WorkerUndefined)); + } +} + +struct VariantRef +{ + VariantRef() : obj(nullptr) {} + VariantRef(const VariantRef &r) : obj(r.obj) { addref(); } + VariantRef(QObject *a) : obj(a) { addref(); } + ~VariantRef() { release(); } + + VariantRef &operator=(const VariantRef &o) { + o.addref(); + release(); + obj = o.obj; + return *this; + } + + void addref() const + { + if (obj) + QMetaObject::invokeMethod(obj, "addref"); + } + + void release() const + { + if (obj) + QMetaObject::invokeMethod(obj, "release"); + + } + + QObject *obj; +}; + +QT_END_NAMESPACE +Q_DECLARE_METATYPE(VariantRef) +Q_DECLARE_METATYPE(QV4::ExecutionEngine *) +QT_BEGIN_NAMESPACE + +ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) +{ + quint32 header = popUint32(data); + Type type = headertype(header); + + Scope scope(engine); + + switch (type) { + case WorkerUndefined: + return QV4::Encode::undefined(); + case WorkerNull: + return QV4::Encode::null(); + case WorkerTrue: + return QV4::Encode(true); + case WorkerFalse: + return QV4::Encode(false); + case WorkerString: + { + quint32 size = headersize(header); + QString qstr((const QChar *)data, size); + data += ALIGN(size * sizeof(quint16)); + return QV4::Encode(engine->newString(qstr)); + } + case WorkerFunction: + Q_ASSERT(!"Unreachable"); + break; + case WorkerArray: + { + quint32 size = headersize(header); + ScopedArrayObject a(scope, engine->newArrayObject()); + ScopedValue v(scope); + for (quint32 ii = 0; ii < size; ++ii) { + v = deserialize(data, engine); + a->put(ii, v); + } + return a.asReturnedValue(); + } + case WorkerObject: + { + quint32 size = headersize(header); + ScopedObject o(scope, engine->newObject()); + ScopedValue name(scope); + ScopedString n(scope); + ScopedValue value(scope); + for (quint32 ii = 0; ii < size; ++ii) { + name = deserialize(data, engine); + value = deserialize(data, engine); + n = name->asReturnedValue(); + o->put(n, value); + } + return o.asReturnedValue(); + } + case WorkerInt32: + return QV4::Encode((qint32)popUint32(data)); + case WorkerUint32: + return QV4::Encode(popUint32(data)); + case WorkerNumber: + return QV4::Encode(popDouble(data)); + case WorkerDate: + return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data)))); + case WorkerRegexp: + { + quint32 flags = headersize(header); + quint32 length = popUint32(data); + QString pattern = QString((const QChar *)data, length - 1); + data += ALIGN(length * sizeof(quint16)); + return Encode(engine->newRegExpObject(pattern, flags)); + } + case WorkerListModel: + { + QObject *agent = reinterpret_cast(popPtr(data)); + QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent)); + // ### Find a better solution then the ugly property + VariantRef ref(agent); + QVariant var = QVariant::fromValue(ref); + QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); + QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); + rv->as()->defineReadonlyProperty(s, v); + + QMetaObject::invokeMethod(agent, "release"); + agent->setProperty("engine", QVariant::fromValue(engine)); + return rv->asReturnedValue(); + } +#if QT_CONFIG(qml_sequence_object) + case WorkerSequence: + { + ScopedValue value(scope); + bool succeeded = false; + quint32 length = headersize(header); + quint32 seqLength = length - 1; + value = deserialize(data, engine); + int sequenceType = value->integerValue(); + ScopedArrayObject array(scope, engine->newArrayObject()); + array->arrayReserve(seqLength); + for (quint32 ii = 0; ii < seqLength; ++ii) { + value = deserialize(data, engine); + array->arrayPut(ii, value); + } + array->setArrayLengthUnchecked(seqLength); + QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); + return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); + } +#endif + } + Q_ASSERT(!"Unreachable"); + return QV4::Encode::undefined(); +} + +QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine) +{ + QByteArray rv; + serialize(rv, value, engine); + return rv; +} + +ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine) +{ + const char *stream = data.constData(); + return deserialize(stream, engine); +} + +QT_END_NAMESPACE diff --git a/src/qmlworkerscript/qv4serialize_p.h b/src/qmlworkerscript/qv4serialize_p.h new file mode 100644 index 0000000000..c8700c3ca5 --- /dev/null +++ b/src/qmlworkerscript/qv4serialize_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SERIALIZE_P_H +#define QV4SERIALIZE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class Serialize { +public: + + static QByteArray serialize(const Value &, ExecutionEngine *); + static ReturnedValue deserialize(const QByteArray &, ExecutionEngine *); + +private: + static void serialize(QByteArray &, const Value &, ExecutionEngine *); + static ReturnedValue deserialize(const char *&, ExecutionEngine *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV8WORKER_P_H diff --git a/src/src.pro b/src/src.pro index bd634247e7..b1e0a72c9f 100644 --- a/src/src.pro +++ b/src/src.pro @@ -7,6 +7,8 @@ SUBDIRS += \ qml \ qmlmodels +qtConfig(qml-worker-script): \ + SUBDIRS += qmlworkerscript qtHaveModule(gui):qtConfig(qml-animation) { SUBDIRS += \ -- cgit v1.2.3 From 803e1d2dc77522665fbfd4ddaa911825bcd50892 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 6 May 2019 12:41:55 +0200 Subject: Move PropertyResolver out of qqmlirbuilder* The "early" compilation doesn't use it and we can get rid of a few V4_BOOTSTRAP checks this way. Change-Id: I1c4845aba445b105ddace0b6810e0e5c28a25b29 Reviewed-by: Simon Hausmann --- src/qml/compiler/compiler.pri | 2 + src/qml/compiler/qqmlirbuilder.cpp | 47 -------------- src/qml/compiler/qqmlirbuilder_p.h | 26 -------- src/qml/compiler/qqmlpropertycachecreator.cpp | 4 +- src/qml/compiler/qqmlpropertycachecreator_p.h | 3 +- src/qml/compiler/qqmlpropertyresolver.cpp | 92 +++++++++++++++++++++++++++ src/qml/compiler/qqmlpropertyresolver_p.h | 87 +++++++++++++++++++++++++ src/qml/compiler/qqmlpropertyvalidator.cpp | 5 +- src/qml/compiler/qqmltypecompiler.cpp | 18 +++--- 9 files changed, 199 insertions(+), 85 deletions(-) create mode 100644 src/qml/compiler/qqmlpropertyresolver.cpp create mode 100644 src/qml/compiler/qqmlpropertyresolver_p.h (limited to 'src') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index da3c173545..0767c4fedf 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -28,6 +28,7 @@ SOURCES += \ !qmldevtools_build { HEADERS += \ + $$PWD/qqmlpropertyresolver_p.h \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qqmlpropertycachecreator_p.h \ $$PWD/qqmlpropertyvalidator_p.h \ @@ -35,6 +36,7 @@ HEADERS += \ SOURCES += \ + $$PWD/qqmlpropertyresolver.cpp \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qqmlpropertycachecreator.cpp \ $$PWD/qqmlpropertyvalidator.cpp \ diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 6e077ec44c..04d116ddee 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1914,53 +1914,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListproperty(name, nullptr, nullptr); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else { - return d; - } -} - - -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QLatin1String("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); - } - - return nullptr; -} - IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) : unit(qmlData) , output(output) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 298fe7dd92..0b462cf4bb 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -508,32 +508,6 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -#ifndef V4_BOOTSTRAP -struct Q_QML_EXPORT PropertyResolver -{ - PropertyResolver(const QQmlRefPointer &cache) - : cache(cache) - {} - - QQmlPropertyData *property(int index) const - { - return cache->property(index); - } - - enum RevisionCheck { - CheckRevision, - IgnoreRevision - }; - - QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, RevisionCheck check = CheckRevision) const; - - // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - - QQmlRefPointer cache; -}; -#endif - struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index fb54da5b73..bd4f2a0612 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -64,7 +64,9 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); + instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache) + .property(instantiatingPropertyName, ¬InRevision, + QQmlPropertyResolver::IgnoreRevision); return instantiatingProperty != nullptr; } diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 346cfb5803..28eea27675 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -53,6 +53,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -323,7 +324,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj int varPropCount = 0; - QmlIR::PropertyResolver resolver(baseTypeCache); + QQmlPropertyResolver resolver(baseTypeCache); auto p = obj->propertiesBegin(); auto pend = obj->propertiesEnd(); diff --git a/src/qml/compiler/qqmlpropertyresolver.cpp b/src/qml/compiler/qqmlpropertyresolver.cpp new file mode 100644 index 0000000000..90eaca0b90 --- /dev/null +++ b/src/qml/compiler/qqmlpropertyresolver.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyresolver_p.h" + +QT_BEGIN_NAMESPACE + +QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision, + RevisionCheck check) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + + // Find the first property + while (d && d->isFunction()) + d = cache->overrideData(d); + + if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else { + return d; + } +} + + +QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + if (notInRevision) *notInRevision = false; + + while (d && !(d->isFunction())) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else if (d && d->isSignal()) { + return d; + } + + if (name.endsWith(QLatin1String("Changed"))) { + QString propName = name.mid(0, name.length() - static_cast(strlen("Changed"))); + + d = property(propName, notInRevision); + if (d) + return cache->signal(d->notifyIndex()); + } + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyresolver_p.h b/src/qml/compiler/qqmlpropertyresolver_p.h new file mode 100644 index 0000000000..df857f242e --- /dev/null +++ b/src/qml/compiler/qqmlpropertyresolver_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYRESOLVER_P_H +#define QQMLPROPERTYRESOLVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_QML_EXPORT QQmlPropertyResolver +{ + QQmlPropertyResolver(const QQmlRefPointer &cache) + : cache(cache) + {} + + QQmlPropertyData *property(int index) const + { + return cache->property(index); + } + + enum RevisionCheck { + CheckRevision, + IgnoreRevision + }; + + QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, + RevisionCheck check = CheckRevision) const; + + // This code must match the semantics of QQmlPropertyPrivate::findSignalByName + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; + + QQmlRefPointer cache; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYRESOLVER_P_H diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 8c06760d42..1812ad6546 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -41,6 +41,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -121,7 +122,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, groupProperties.insert(pos, binding); } - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QString defaultPropertyName; QQmlPropertyData *defaultProperty = nullptr; @@ -164,7 +165,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, pd = propertyResolver.signal(name, ¬InRevision); } else { pd = propertyResolver.property(name, ¬InRevision, - QmlIR::PropertyResolver::CheckRevision); + QQmlPropertyResolver::CheckRevision); } if (notInRevision) { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 239f04a58f..db57d38c24 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -357,7 +358,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName)) continue; - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); propertyName.remove(0, 2); @@ -513,7 +514,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() continue; const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression @@ -722,7 +723,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases() const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { @@ -754,7 +755,7 @@ void QQmlScriptStringScanner::scan() const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { @@ -799,7 +800,7 @@ static bool isUsableComponent(const QMetaObject *metaObject) void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) { - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); @@ -1110,7 +1111,7 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv break; } - QmlIR::PropertyResolver resolver(targetCache); + QQmlPropertyResolver resolver(targetCache); QQmlPropertyData *targetProperty = resolver.property(property.toString()); @@ -1224,7 +1225,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QStringList deferredPropertyNames; { @@ -1263,7 +1264,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) continue; bool notInRevision = false; - pd = propertyResolver.property(name, ¬InRevision, QmlIR::PropertyResolver::CheckRevision); + pd = propertyResolver.property(name, ¬InRevision, + QQmlPropertyResolver::CheckRevision); } bool seenSubObjectWithId = false; -- cgit v1.2.3 From 791e63021e1e1333ff3c678674df8f4f18212e43 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 6 May 2019 13:06:15 +0200 Subject: Move QQmlIR::IRLoader out of qqmlirbuilder* We don't need it to build the IR and we can drop a few checks for V4_BOOTSTRAP this way. Change-Id: I9464e65528c70c42ebc8ddad576eaab001dc9d2f Reviewed-by: Simon Hausmann --- src/qml/compiler/compiler.pri | 2 + src/qml/compiler/qqmlirbuilder.cpp | 164 ----------------------------- src/qml/compiler/qqmlirbuilder_p.h | 19 +--- src/qml/compiler/qqmlirloader.cpp | 205 +++++++++++++++++++++++++++++++++++++ src/qml/compiler/qqmlirloader_p.h | 80 +++++++++++++++ src/qml/qml/qqmltypeloader.cpp | 5 +- 6 files changed, 292 insertions(+), 183 deletions(-) create mode 100644 src/qml/compiler/qqmlirloader.cpp create mode 100644 src/qml/compiler/qqmlirloader_p.h (limited to 'src') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 0767c4fedf..c15c45a1cb 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -28,6 +28,7 @@ SOURCES += \ !qmldevtools_build { HEADERS += \ + $$PWD/qqmlirloader_p.h \ $$PWD/qqmlpropertyresolver_p.h \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qqmlpropertycachecreator_p.h \ @@ -36,6 +37,7 @@ HEADERS += \ SOURCES += \ + $$PWD/qqmlirloader.cpp \ $$PWD/qqmlpropertyresolver.cpp \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qqmlpropertycachecreator.cpp \ diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 04d116ddee..28e7daecd5 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1911,167 +1911,3 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListjsParserEngine.pool(); -} - -void IRLoader::load() -{ - output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - - const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); - - for (quint32 i = 0; i < qmlUnit->nImports; ++i) - output->imports << qmlUnit->importAt(i); - - if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { - QmlIR::Pragma *p = New(); - p->location = QV4::CompiledData::Location(); - p->type = QmlIR::Pragma::PragmaSingleton; - output->pragmas << p; - } - - for (uint i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); - QmlIR::Object *object = loadObject(serializedObject); - output->objects.append(object); - } -} - -struct FakeExpression : public QQmlJS::AST::NullExpression -{ - FakeExpression(int start, int length) - : location(start, length) - {} - - virtual QQmlJS::AST::SourceLocation firstSourceLocation() const - { return location; } - - virtual QQmlJS::AST::SourceLocation lastSourceLocation() const - { return location; } - -private: - QQmlJS::AST::SourceLocation location; -}; - -QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) -{ - QmlIR::Object *object = pool->New(); - object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); - - object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->flags = serializedObject->flags; - object->id = serializedObject->id; - object->location = serializedObject->location; - object->locationOfIdProperty = serializedObject->locationOfIdProperty; - - QVector functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New(); - *static_cast(b) = serializedObject->bindingTable()[i]; - object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { - functionIndices.append(b->value.compiledScriptIndex); - b->value.compiledScriptIndex = functionIndices.count() - 1; - - QmlIR::CompiledFunctionOrExpression *foe = pool->New(); - foe->nameIndex = 0; - - QQmlJS::AST::ExpressionNode *expr; - - if (b->stringIndex != quint32(0)) { - const int start = output->code.length(); - const QString script = output->stringAt(b->stringIndex); - const int length = script.length(); - output->code.append(script); - expr = new (pool) FakeExpression(start, length); - } else - expr = new (pool) QQmlJS::AST::NullExpression(); - foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy - object->functionsAndExpressions->append(foe); - } - } - - Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); - - for (uint i = 0; i < serializedObject->nSignals; ++i) { - const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); - QmlIR::Signal *s = pool->New(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New(); - *static_cast(p) = *serializedSignal->parameterAt(i); - s->parameters->append(p); - } - - object->qmlSignals->append(s); - } - - for (uint i = 0; i < serializedObject->nEnums; ++i) { - const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); - QmlIR::Enum *e = pool->New(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New(); - *static_cast(v) = *serializedEnum->enumValueAt(i); - e->enumValues->append(v); - } - - object->qmlEnums->append(e); - } - - const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); - for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { - QmlIR::Property *p = pool->New(); - *static_cast(p) = *serializedProperty; - object->properties->append(p); - } - - { - const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); - for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { - QmlIR::Alias *a = pool->New(); - *static_cast(a) = *serializedAlias; - object->aliases->append(a); - } - } - - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); - for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { - QmlIR::Function *f = pool->New(); - const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); - - functionIndices.append(*functionIdx); - f->index = functionIndices.count() - 1; - f->location = compiledFunction->location; - f->nameIndex = compiledFunction->nameIndex; - - f->formals.allocate(pool, int(compiledFunction->nFormals)); - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); - for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) - f->formals[i] = *formalNameIdx; - - object->functions->append(f); - } - - object->runtimeFunctionIndices.allocate(pool, functionIndices); - - return object; -} - -#endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 0b462cf4bb..c937158a33 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -65,11 +65,11 @@ QT_BEGIN_NAMESPACE class QQmlPropertyCache; class QQmlContextData; class QQmlTypeNameCache; +struct QQmlIRLoader; namespace QmlIR { struct Document; -struct IRLoader; template struct PoolList @@ -347,7 +347,7 @@ public: const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } private: - friend struct IRLoader; + friend struct ::QQmlIRLoader; PoolList *properties; PoolList *aliases; @@ -524,21 +524,6 @@ private: const QV4::Compiler::StringTableGenerator *stringPool; }; -struct Q_QML_PRIVATE_EXPORT IRLoader { - IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); - - void load(); - -private: - QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); - - template _Tp *New() { return pool->New<_Tp>(); } - - const QV4::CompiledData::Unit *unit; - QmlIR::Document *output; - QQmlJS::MemoryPool *pool; -}; - } // namespace QmlIR struct QQmlCompileError diff --git a/src/qml/compiler/qqmlirloader.cpp b/src/qml/compiler/qqmlirloader.cpp new file mode 100644 index 0000000000..c2eb6da2fa --- /dev/null +++ b/src/qml/compiler/qqmlirloader.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlirloader_p.h" +#include + +QT_BEGIN_NAMESPACE + +QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void QQmlIRLoader::load() +{ + output->jsGenerator.stringTable.initializeFromBackingUnit(unit); + + const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); + + for (quint32 i = 0; i < qmlUnit->nImports; ++i) + output->imports << qmlUnit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + for (uint i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + object->flags = serializedObject->flags; + object->id = serializedObject->id; + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New(); + *static_cast(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New(); + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::SignalParameter *p = pool->New(); + *static_cast(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + for (uint i = 0; i < serializedObject->nEnums; ++i) { + const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); + QmlIR::Enum *e = pool->New(); + e->nameIndex = serializedEnum->nameIndex; + e->location = serializedEnum->location; + e->enumValues = pool->New >(); + + for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { + QmlIR::EnumValue *v = pool->New(); + *static_cast(v) = *serializedEnum->enumValueAt(i); + e->enumValues->append(v); + } + + object->qmlEnums->append(e); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New(); + *static_cast(p) = *serializedProperty; + object->properties->append(p); + } + + { + const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); + for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { + QmlIR::Alias *a = pool->New(); + *static_cast(a) = *serializedAlias; + object->aliases->append(a); + } + } + + const quint32_le *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + f->index = functionIndices.count() - 1; + f->location = compiledFunction->location; + f->nameIndex = compiledFunction->nameIndex; + + f->formals.allocate(pool, int(compiledFunction->nFormals)); + const quint32_le *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) + f->formals[i] = *formalNameIdx; + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlirloader_p.h b/src/qml/compiler/qqmlirloader_p.h new file mode 100644 index 0000000000..cf46ca3cb0 --- /dev/null +++ b/src/qml/compiler/qqmlirloader_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLIRLOADER_P_H +#define QQMLIRLOADER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +struct Object; +} + +struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { + QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + +QT_END_NAMESPACE + +#endif // QQMLIRLOADER_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 457558fb56..1f80e1905e 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -2502,7 +2503,7 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) { m_document.reset(new QmlIR::Document(isDebugging())); - QmlIR::IRLoader loader(unit, m_document.data()); + QQmlIRLoader loader(unit, m_document.data()); loader.load(); m_document->jsModule.fileName = urlString(); m_document->jsModule.finalUrl = finalUrlString(); @@ -2544,7 +2545,7 @@ bool QQmlTypeData::loadFromSource() void QQmlTypeData::restoreIR(QQmlRefPointer unit) { m_document.reset(new QmlIR::Document(isDebugging())); - QmlIR::IRLoader loader(unit->unitData(), m_document.data()); + QQmlIRLoader loader(unit->unitData(), m_document.data()); loader.load(); m_document->jsModule.fileName = urlString(); m_document->jsModule.finalUrl = finalUrlString(); -- cgit v1.2.3 From 3df85008591dffc64427095b022421469cb9a866 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 30 Apr 2019 11:22:23 +0200 Subject: Avoid std::function in qqmlirbuilder.cpp Some compilers seem to miscompile this construction. Furthermore, it doesn't really add to the readability of the code. Inline the code in question at the only place it's used and avoid most of the const_cast by adding a non-const accessor to CompiledData::Unit. Fixes: QTBUG-75392 Change-Id: I015317f28a92817d08d616cc35956745758d7847 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 36 ++++++++++++++---------------------- src/qml/compiler/qv4compileddata_p.h | 4 ++++ 2 files changed, 18 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 868f600a10..558399ad6c 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1558,15 +1558,12 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen QQmlRefPointer compilationUnit = output.javaScriptCompilationUnit; - const QV4::CompiledData::Unit *jsUnit = nullptr; - std::function unitFinalizer - = [](QV4::CompiledData::QmlUnit *unit, uint) { - return unit; - }; + QV4::CompiledData::Unit *jsUnit = nullptr; + const bool finalize = !compilationUnit->data; // We may already have unit data if we're loading an ahead-of-time generated cache file. - if (compilationUnit->data) { - jsUnit = compilationUnit->data; + if (!finalize) { + jsUnit = const_cast(compilationUnit->data); #ifndef V4_BOOTSTRAP output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); #endif @@ -1599,20 +1596,6 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen #endif createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); - - // Combine the qml data into the general unit data. - unitFinalizer = [&jsUnit](QV4::CompiledData::QmlUnit *qmlUnit, uint qmlDataSize) { - void *ptr = const_cast(jsUnit); - QV4::CompiledData::Unit *newUnit = (QV4::CompiledData::Unit *)realloc(ptr, jsUnit->unitSize + qmlDataSize); - jsUnit = newUnit; - newUnit->offsetToQmlUnit = newUnit->unitSize; - newUnit->unitSize += qmlDataSize; - memcpy(const_cast(newUnit->qmlUnit()), qmlUnit, qmlDataSize); - free(const_cast(qmlUnit)); - qmlUnit = nullptr; - newUnit->generateChecksum(); - return const_cast(newUnit->qmlUnit()); - }; } // No more new strings after this point, we're calculating offsets. @@ -1779,7 +1762,16 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen } } - qmlUnit = unitFinalizer(qmlUnit, totalSize); + if (finalize) { + // Combine the qml data into the general unit data. + jsUnit = static_cast(realloc(jsUnit, jsUnit->unitSize + totalSize)); + jsUnit->offsetToQmlUnit = jsUnit->unitSize; + jsUnit->unitSize += totalSize; + memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize); + free(qmlUnit); + jsUnit->generateChecksum(); + qmlUnit = jsUnit->qmlUnit(); + } static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS"); if (showStats) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 3fd9fdf74b..76795f7aa3 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -891,6 +891,10 @@ struct Unit return reinterpret_cast(reinterpret_cast(this) + offsetToQmlUnit); } + QmlUnit *qmlUnit() { + return reinterpret_cast(reinterpret_cast(this) + offsetToQmlUnit); + } + bool isSingleton() const { return flags & Unit::IsSingleton; } -- cgit v1.2.3 From 4a4842118d2303a8d851d1d8b85fe182d3fe492a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 6 May 2019 16:03:37 +0200 Subject: Accessibility: Make sure StaticText is marked read-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test more of the text properties. This is still very incomplete, but a small step forward. Also make sure that editable text reports the editable state. Fixes: QTBUG-75002 Change-Id: I9e43c980d8fa91671acb4e40e5d9162854884ee7 Reviewed-by: Jan Arve Sæther --- src/quick/accessible/qaccessiblequickitem.cpp | 16 ++++++++++++++++ src/quick/accessible/qaccessiblequickitem_p.h | 1 + src/quick/items/qquickaccessibleattached_p.h | 8 +++++++- 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp index 98e7663c96..b87203c3ef 100644 --- a/src/quick/accessible/qaccessiblequickitem.cpp +++ b/src/quick/accessible/qaccessiblequickitem.cpp @@ -382,6 +382,22 @@ QString QAccessibleQuickItem::text(QAccessible::Text textType) const return QString(); } +void QAccessibleQuickItem::setText(QAccessible::Text textType, const QString &text) +{ + if (role() != QAccessible::EditableText) + return; + if (textType != QAccessible::Value) + return; + + if (QTextDocument *doc = textDocument()) { + doc->setPlainText(text); + return; + } + auto textPropertyName = "text"; + if (object()->metaObject()->indexOfProperty(textPropertyName) >= 0) + object()->setProperty(textPropertyName, text); +} + void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t) { QAccessible::Role r = role(); diff --git a/src/quick/accessible/qaccessiblequickitem_p.h b/src/quick/accessible/qaccessiblequickitem_p.h index 5375d37bf0..931e995f0f 100644 --- a/src/quick/accessible/qaccessiblequickitem_p.h +++ b/src/quick/accessible/qaccessiblequickitem_p.h @@ -83,6 +83,7 @@ public: QAccessible::State state() const override; QAccessible::Role role() const override; QString text(QAccessible::Text) const override; + void setText(QAccessible::Text, const QString &text) override; bool isAccessible() const; diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 215a1e5db6..e292c280df 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -128,13 +128,19 @@ public: case QAccessible::Button: case QAccessible::MenuItem: case QAccessible::PageTab: - case QAccessible::EditableText: case QAccessible::SpinBox: case QAccessible::ComboBox: case QAccessible::Terminal: case QAccessible::ScrollBar: m_state.focusable = true; break; + case QAccessible::EditableText: + m_state.editable = true; + m_state.focusable = true; + break; + case QAccessible::StaticText: + m_state.readOnly = true; + break; default: break; } -- cgit v1.2.3 From cf4a7fa44403bb3bae9c98afb80222d5e03798c5 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 6 May 2019 12:19:07 +0200 Subject: Remove the bootstrap code from assembler and JIT We don't build the assembler or the JIT in bootstrap mode. Change-Id: Idc3a56cc1e9cfba415bef9cba221c8a60ee75010 Reviewed-by: Simon Hausmann --- src/3rdparty/masm/assembler/ARM64Assembler.h | 7 +-- src/3rdparty/masm/assembler/ARMv7Assembler.h | 25 +------- .../masm/assembler/AbstractMacroAssembler.h | 14 ++--- src/3rdparty/masm/assembler/LinkBuffer.h | 6 +- src/3rdparty/masm/assembler/MacroAssembler.h | 14 +---- src/3rdparty/masm/assembler/MacroAssemblerARM64.h | 31 +--------- src/3rdparty/masm/assembler/MacroAssemblerARMv7.h | 71 +--------------------- src/3rdparty/masm/assembler/MacroAssemblerX86.h | 32 ---------- src/3rdparty/masm/assembler/MacroAssemblerX86_64.h | 27 -------- src/3rdparty/masm/assembler/X86Assembler.h | 41 ------------- src/qml/jit/qv4assemblercommon_p.h | 2 +- 11 files changed, 20 insertions(+), 250 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/assembler/ARM64Assembler.h b/src/3rdparty/masm/assembler/ARM64Assembler.h index a9166e83a2..ca6b33d39a 100644 --- a/src/3rdparty/masm/assembler/ARM64Assembler.h +++ b/src/3rdparty/masm/assembler/ARM64Assembler.h @@ -26,7 +26,7 @@ #ifndef ARM64Assembler_h #define ARM64Assembler_h -#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP)) +#if ENABLE(ASSEMBLER) && CPU(ARM64) #include "AssemblerBuffer.h" #include "AbstractMacroAssembler.h" @@ -3021,10 +3021,7 @@ public: static void cacheFlush(void* code, size_t size) { -#if defined(V4_BOOTSTRAP) - UNUSED_PARAM(code) - UNUSED_PARAM(size) -#elif OS(IOS) +#if OS(IOS) sys_cache_control(kCacheFunctionPrepareForExecution, code, size); #elif OS(LINUX) size_t page = pageSize(); diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h index d57e5a7c78..f2e8dc1a1b 100644 --- a/src/3rdparty/masm/assembler/ARMv7Assembler.h +++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h @@ -27,7 +27,7 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#if ENABLE(ASSEMBLER) && (CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)) +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) #include "AssemblerBuffer.h" #include "MacroAssemblerCodeRef.h" @@ -2166,7 +2166,6 @@ public: linkJumpAbsolute(location, to); } -#if !defined(V4_BOOTSTRAP) static void linkCall(void* code, AssemblerLabel from, void* to) { ASSERT(!(reinterpret_cast(code) & 1)); @@ -2175,14 +2174,12 @@ public: setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); } -#endif static void linkPointer(void* code, AssemblerLabel where, void* value) { setPointer(reinterpret_cast(code) + where.m_offset, value, false); } -#if !defined(V4_BOOTSTRAP) static void relinkJump(void* from, void* to) { ASSERT(!(reinterpret_cast(from) & 1)); @@ -2205,7 +2202,6 @@ public: { return readPointer(reinterpret_cast(from) - 1); } -#endif static void repatchInt32(void* where, int32_t value) { @@ -2234,7 +2230,6 @@ public: cacheFlush(location, sizeof(uint16_t) * 2); } -#if !defined(V4_BOOTSTRAP) static void repatchPointer(void* where, void* value) { ASSERT(!(reinterpret_cast(where) & 1)); @@ -2246,7 +2241,6 @@ public: { return reinterpret_cast(readInt32(where)); } -#endif static void replaceWithJump(void* instructionStart, void* to) { @@ -2321,7 +2315,7 @@ public: unsigned debugOffset() { return m_formatter.debugOffset(); } -#if OS(LINUX) && !defined(V4_BOOTSTRAP) +#if OS(LINUX) static inline void linuxPageFlush(uintptr_t begin, uintptr_t end) { asm volatile( @@ -2341,10 +2335,7 @@ public: static void cacheFlush(void* code, size_t size) { -#if defined(V4_BOOTSTRAP) - UNUSED_PARAM(code) - UNUSED_PARAM(size) -#elif OS(IOS) +#if OS(IOS) sys_cache_control(kCacheFunctionPrepareForExecution, code, size); #elif OS(LINUX) size_t page = pageSize(); @@ -2662,11 +2653,6 @@ private: static void linkBX(uint16_t* instruction, void* target) { -#if defined(V4_BOOTSTRAP) - UNUSED_PARAM(instruction); - UNUSED_PARAM(target); - RELEASE_ASSERT_NOT_REACHED(); -#else // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); @@ -2679,7 +2665,6 @@ private: instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); -#endif } void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) @@ -2712,9 +2697,6 @@ private: instruction[-3] = OP_NOP_T2b; linkJumpT4(instruction, target); } else { -#if defined(V4_BOOTSTRAP) - RELEASE_ASSERT_NOT_REACHED(); -#else const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); @@ -2723,7 +2705,6 @@ private: instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); -#endif } } diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h index d0c1c4613e..14644a4193 100644 --- a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h +++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h @@ -66,7 +66,7 @@ public: typedef MacroAssemblerCodePtr CodePtr; typedef MacroAssemblerCodeRef CodeRef; -#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) && !CPU(ARM64) class Jump; #endif @@ -328,7 +328,7 @@ public: friend class AbstractMacroAssembler; friend struct DFG::OSRExit; -#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP) +#if CPU(ARM_THUMB2) || CPU(ARM64) using Jump = typename AssemblerType::template Jump