aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/masm/assembler/X86Assembler.h146
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp3
-rw-r--r--src/imports/builtins/CMakeLists.txt7
-rw-r--r--src/imports/builtins/builtins.qmltypes28
-rw-r--r--src/labs/models/qqmltablemodel_p.h1
-rw-r--r--src/labs/models/qqmltablemodelcolumn_p.h1
-rw-r--r--src/particles/qquickparticleaffector_p.h1
-rw-r--r--src/particles/qquickparticlesystem_p.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt2
-rw-r--r--src/plugins/scenegraph/openvg/CMakeLists.txt2
-rw-r--r--src/qml/CMakeLists.txt5
-rw-r--r--src/qml/Qt6AndroidQmlMacros.cmake9
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake1
-rw-r--r--src/qml/Qt6QmlDeploySupport.cmake4
-rw-r--r--src/qml/Qt6QmlMacros.cmake75
-rw-r--r--src/qml/common/qv4compileddata_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h3
-rw-r--r--src/qml/debugger/qqmldebug.h1
-rw-r--r--src/qml/doc/snippets/code/doc_src_qtqml.cmake6
-rw-r--r--src/qml/doc/snippets/qmltc/CMakeLists.txt4
-rw-r--r--src/qml/doc/snippets/qmltc/myApp.qml1
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp6
-rw-r--r--src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp3
-rw-r--r--src/qml/doc/src/cmake/cmake-properties.qdoc187
-rw-r--r--src/qml/doc/src/cmake/cmake-variables.qdoc1
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc12
-rw-r--r--src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc22
-rw-r--r--src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc5
-rw-r--r--src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc (renamed from src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc)23
-rw-r--r--src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc2
-rw-r--r--src/qml/doc/src/cmake/qt_query_qml_module.qdoc3
-rw-r--r--src/qml/doc/src/cmake/qt_target_qml_sources.qdoc2
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc6
-rw-r--r--src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake4
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc18
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc8
-rw-r--r--src/qml/doc/src/qtqml.qdoc7
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsapi/qjsengine.cpp140
-rw-r--r--src/qml/jsapi/qjsengine.h28
-rw-r--r--src/qml/jsruntime/qv4engine.cpp139
-rw-r--r--src/qml/jsruntime/qv4engine_p.h17
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp7
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h2
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h6
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp5
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h1
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference.cpp5
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference_p.h2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp41
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h9
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp14
-rw-r--r--src/qml/parser/qqmljs.g3
-rw-r--r--src/qml/parser/qqmljslexer.cpp877
-rw-r--r--src/qml/parser/qqmljslexer_p.h117
-rw-r--r--src/qml/qml/qqml.cpp354
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp26
-rw-r--r--src/qml/qml/qqmlcomponent.h3
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h11
-rw-r--r--src/qml/qml/qqmlengine.cpp38
-rw-r--r--src/qml/qml/qqmlguard_p.h111
-rw-r--r--src/qml/qml/qqmlimport.cpp34
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp42
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp1
-rw-r--r--src/qml/qml/qqmlpluginimporter.cpp8
-rw-r--r--src/qml/qml/qqmlproperty.h2
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp24
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h3
-rw-r--r--src/qml/qml/qqmltypedata.cpp10
-rw-r--r--src/qml/qml/qqmltypeloader.cpp1
-rw-r--r--src/qml/qml/qqmltypeloader_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp21
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h5
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp17
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h8
-rw-r--r--src/qmlcompiler/CMakeLists.txt5
-rw-r--r--src/qmlcompiler/qdeferredpointer_p.h20
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp397
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h102
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp1130
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h28
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h140
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp25
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp18
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp18
-rw-r--r--src/qmlcompiler/qqmljsimporter_p.h11
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp434
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h3
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp22
-rw-r--r--src/qmlcompiler/qqmljsloadergenerator.cpp2
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp133
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h90
-rw-r--r--src/qmlcompiler/qqmljsmetatypes.cpp108
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h240
-rw-r--r--src/qmlcompiler/qqmljsregistercontent.cpp22
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h48
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp11
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h12
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp12
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp44
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp2
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp994
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h27
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp678
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h67
-rw-r--r--src/qmldebug/qqmldebugconnection.cpp1
-rw-r--r--src/qmldebug/qqmlenginecontrolclient_p_p.h2
-rw-r--r--src/qmldom/qqmldomlinewriter_p.h1
-rw-r--r--src/qmldom/qqmldomtypesreader.cpp2
-rw-r--r--src/qmlintegration/qqmlintegration.h2
-rw-r--r--src/qmlmodels/CMakeLists.txt5
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp8
-rw-r--r--src/qmlmodels/qqmladaptormodel_p.h4
-rw-r--r--src/qmlmodels/qqmlmodelindexvaluetype.cpp (renamed from src/qml/types/qqmlmodelindexvaluetype.cpp)0
-rw-r--r--src/qmlmodels/qqmlmodelindexvaluetype_p.h (renamed from src/qml/types/qqmlmodelindexvaluetype_p.h)0
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel.cpp57
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel_p_p.h4
-rw-r--r--src/qmlmodels/qquickpackage.cpp9
-rw-r--r--src/qmltest/CMakeLists.txt2
-rw-r--r--src/qmltest/quicktest.cpp2
-rw-r--r--src/qmltest/quicktestresult_p.h2
-rw-r--r--src/qmltest/quicktestutil.cpp2
-rw-r--r--src/qmltyperegistrar/qmltyperegistrar.cpp17
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp34
-rw-r--r--src/quick/CMakeLists.txt4
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp7
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc12
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc (renamed from src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc)0
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc1
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc14
-rw-r--r--src/quick/doc/src/qt6-changes.qdoc4
-rw-r--r--src/quick/doc/src/qtquick.qdoc27
-rw-r--r--src/quick/items/context2d/qquickcanvascontext_p.h6
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h1
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h5
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp5
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h2
-rw-r--r--src/quick/items/qquickitem.h1
-rw-r--r--src/quick/items/qquickloader.cpp12
-rw-r--r--src/quick/items/qquickloader_p.h1
-rw-r--r--src/quick/items/qquickloader_p_p.h4
-rw-r--r--src/quick/items/qquickrendercontrol.cpp1
-rw-r--r--src/quick/items/qquickrendercontrol.h2
-rw-r--r--src/quick/items/qquickstateoperations.cpp69
-rw-r--r--src/quick/items/qquicktableview.cpp326
-rw-r--r--src/quick/items/qquicktableview_p.h6
-rw-r--r--src/quick/items/qquicktableview_p_p.h48
-rw-r--r--src/quick/items/qquicktext.cpp49
-rw-r--r--src/quick/items/qquicktreeview.cpp315
-rw-r--r--src/quick/items/qquicktreeview_p.h16
-rw-r--r--src/quick/items/qquicktreeview_p_p.h1
-rw-r--r--src/quick/items/qquickview_p.h5
-rw-r--r--src/quick/items/qquickwindow.cpp95
-rw-r--r--src/quick/items/qquickwindow_p.h3
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp54
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp17
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h1
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h1
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp3
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h2
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp4
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp89
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h6
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture.cpp9
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp2
-rw-r--r--src/quick/util/qquickapplication.cpp31
-rw-r--r--src/quick/util/qquickapplication_p.h3
-rw-r--r--src/quick/util/qquickfontloader.cpp12
-rw-r--r--src/quick/util/qquickforeignutils_p.h9
-rw-r--r--src/quick/util/qquickinputmethod.cpp159
-rw-r--r--src/quick/util/qquickinputmethod_p.h122
-rw-r--r--src/quick/util/qquickpath_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp114
-rw-r--r--src/quick/util/qquickpixmapcache_p.h3
-rw-r--r--src/quick/util/qquickstate_p_p.h12
-rw-r--r--src/quick/util/qquickstategroup.cpp10
-rw-r--r--src/quick/util/qquickvalidator.cpp6
-rw-r--r--src/quickcontrols2/CMakeLists.txt3
-rw-r--r--src/quickcontrols2/basic/DayOfWeekRow.qml4
-rw-r--r--src/quickcontrols2/basic/MonthGrid.qml4
-rw-r--r--src/quickcontrols2/basic/WeekNumberColumn.qml4
-rw-r--r--src/quickcontrols2/doc/src/qt6-changes.qdoc16
-rw-r--r--src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc8
-rw-r--r--src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc14
-rw-r--r--src/quickcontrols2/material/qquickmaterialstyle_p.h2
-rw-r--r--src/quickcontrols2impl/qquickiconlabel.cpp2
-rw-r--r--src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc4
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp1
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp9
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h1
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml4
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml19
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml16
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp8
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp12
-rw-r--r--src/quicknativestyle/items/qquickstyleitem.h2
-rw-r--r--src/quicktemplates2/qquickabstractbutton.cpp6
-rw-r--r--src/quicktemplates2/qquickaction.cpp2
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp50
-rw-r--r--src/quicktemplates2/qquickicon.cpp69
-rw-r--r--src/quicktemplates2/qquickicon_p.h5
-rw-r--r--src/quicktemplates2/qquickmenuitem.cpp3
-rw-r--r--src/quicktemplates2/qquickpresshandler.cpp3
-rw-r--r--src/quicktemplates2/qquickscrollbar.cpp20
-rw-r--r--src/quicktemplates2/qquickspinbox.cpp3
-rw-r--r--src/quicktemplates2/qquicktreeviewdelegate.cpp7
-rw-r--r--src/quickwidgets/qquickwidget.cpp45
215 files changed, 6660 insertions, 3168 deletions
diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h
index 1061021227..07a883397a 100644
--- a/src/3rdparty/masm/assembler/X86Assembler.h
+++ b/src/3rdparty/masm/assembler/X86Assembler.h
@@ -20,7 +20,7 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef X86Assembler_h
@@ -164,7 +164,7 @@ private:
OP_HLT = 0xF4,
OP_GROUP3_EbIb = 0xF6,
OP_GROUP3_Ev = 0xF7,
- OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
+ OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
OP_GROUP5_Ev = 0xFF,
} OneByteOpcodeID;
@@ -220,12 +220,12 @@ private:
GROUP1_OP_CMP = 7,
GROUP1A_OP_POP = 0,
-
+
GROUP2_OP_ROL = 0,
GROUP2_OP_ROR = 1,
GROUP2_OP_RCL = 2,
GROUP2_OP_RCR = 3,
-
+
GROUP2_OP_SHL = 4,
GROUP2_OP_SHR = 5,
GROUP2_OP_SAR = 7,
@@ -246,7 +246,7 @@ private:
ESCAPE_DD_FSTP_doubleReal = 3,
} GroupOpcodeID;
-
+
class X86InstructionFormatter;
public:
@@ -308,7 +308,7 @@ public:
{
m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset);
}
-
+
#if !CPU(X86_64)
void addl_mr(const void* addr, RegisterID dst)
{
@@ -577,7 +577,7 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void subl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
@@ -671,12 +671,12 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void xorq_rm(RegisterID src, int offset, RegisterID base)
{
m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset);
}
-
+
void rorq_i8r(int imm, RegisterID dst)
{
if (imm == 1)
@@ -749,7 +749,7 @@ public:
{
m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
}
-
+
void shrl_i8r(int imm, RegisterID dst)
{
if (imm == 1)
@@ -759,7 +759,7 @@ public:
m_formatter.immediate8(imm);
}
}
-
+
void shrl_CLr(RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst);
@@ -834,7 +834,7 @@ public:
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
m_formatter.immediate32(imm);
}
-
+
void cmpl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
@@ -845,19 +845,19 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void cmpb_im(int imm, int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset);
m_formatter.immediate8(imm);
}
-
+
void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset);
m_formatter.immediate8(imm);
}
-
+
#if CPU(X86)
void cmpb_im(int imm, const void* addr)
{
@@ -985,7 +985,7 @@ public:
{
m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
}
-
+
void testl_i32r(int imm, RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst);
@@ -1008,7 +1008,7 @@ public:
m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset);
m_formatter.immediate8(imm);
}
-
+
void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset);
@@ -1057,14 +1057,14 @@ public:
m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
m_formatter.immediate32(imm);
}
-#endif
+#endif
void testw_rr(RegisterID src, RegisterID dst)
{
m_formatter.prefix(PRE_OPERAND_SIZE);
m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
}
-
+
void testb_i8r(int imm, RegisterID dst)
{
m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst);
@@ -1124,7 +1124,7 @@ public:
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, dst);
}
-
+
void movl_rm(RegisterID src, int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
@@ -1139,7 +1139,7 @@ public:
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
}
-
+
void movl_mEAX(const void* addr)
{
m_formatter.oneByteOp(OP_MOV_EAXOv);
@@ -1159,7 +1159,7 @@ public:
{
m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset);
}
-
+
void movl_mr_disp8(int offset, RegisterID base, RegisterID dst)
{
m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset);
@@ -1181,7 +1181,7 @@ public:
m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset);
m_formatter.immediate32(imm);
}
-
+
void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset);
@@ -1210,12 +1210,12 @@ public:
m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset);
m_formatter.immediate8(imm);
}
-
+
void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset);
}
-
+
void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_OPERAND_SIZE);
@@ -1296,22 +1296,22 @@ public:
m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
m_formatter.immediate64(imm);
}
-
+
void movsxd_rr(RegisterID src, RegisterID dst)
{
m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src);
}
-
-
+
+
#else
void movl_rm(RegisterID src, const void* addr)
{
if (src == X86Registers::eax)
movl_EAXm(addr);
- else
+ else
m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
}
-
+
void movl_mr(const void* addr, RegisterID dst)
{
if (dst == X86Registers::eax)
@@ -1351,7 +1351,7 @@ public:
{
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
}
-
+
void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
{
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
@@ -1361,7 +1361,7 @@ public:
{
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
}
-
+
void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
{
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
@@ -1404,13 +1404,13 @@ public:
m_formatter.oneByteOp(OP_CALL_rel32);
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel call(RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst);
return m_formatter.label();
}
-
+
void call_m(int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset);
@@ -1421,7 +1421,7 @@ public:
m_formatter.oneByteOp(OP_JMP_rel32);
return m_formatter.immediateRel32();
}
-
+
// Return a AssemblerLabel so we have a label to the jump, so we can use this
// To make a tail recursive call on x86-64. The MacroAssembler
// really shouldn't wrap this as a Jump, since it can't be linked. :-/
@@ -1430,12 +1430,12 @@ public:
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst);
return m_formatter.label();
}
-
+
void jmp_m(int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset);
}
-
+
#if !CPU(X86_64)
void jmp_m(const void* address)
{
@@ -1448,7 +1448,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionNE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jnz()
{
return jne();
@@ -1459,7 +1459,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jz()
{
return je();
@@ -1470,25 +1470,25 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionL));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jb()
{
m_formatter.twoByteOp(jccRel32(ConditionB));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jle()
{
m_formatter.twoByteOp(jccRel32(ConditionLE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jbe()
{
m_formatter.twoByteOp(jccRel32(ConditionBE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jge()
{
m_formatter.twoByteOp(jccRel32(ConditionGE));
@@ -1506,13 +1506,13 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionA));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jae()
{
m_formatter.twoByteOp(jccRel32(ConditionAE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jo()
{
m_formatter.twoByteOp(jccRel32(ConditionO));
@@ -1530,7 +1530,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionP));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel js()
{
m_formatter.twoByteOp(jccRel32(ConditionS));
@@ -1611,7 +1611,7 @@ public:
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src);
}
-
+
#if CPU(X86_64)
void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst)
{
@@ -1657,19 +1657,19 @@ public:
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
}
-
+
void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
}
-
+
void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
}
-
+
void movsd_mr(int offset, RegisterID base, XMMRegisterID dst)
{
m_formatter.prefix(PRE_SSE_F2);
@@ -1681,7 +1681,7 @@ public:
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset);
}
-
+
void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
{
m_formatter.prefix(PRE_SSE_F3);
@@ -1800,7 +1800,7 @@ public:
{
m_formatter.oneByteOp(OP_INT3);
}
-
+
void ret()
{
m_formatter.oneByteOp(OP_RET);
@@ -1817,7 +1817,7 @@ public:
{
return m_formatter.codeSize();
}
-
+
AssemblerLabel labelForWatchpoint()
{
AssemblerLabel result = m_formatter.label();
@@ -1827,7 +1827,7 @@ public:
m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize();
return result;
}
-
+
AssemblerLabel labelIgnoringWatchpoints()
{
return m_formatter.label();
@@ -1877,7 +1877,7 @@ public:
memcpy(&val, t_ptr, sizeof(T));
return val;
}
-
+
static void linkJump(void* code, AssemblerLabel from, void* to)
{
ASSERT(from.isSet());
@@ -1903,12 +1903,12 @@ public:
{
setRel32(from, to);
}
-
+
static void relinkCall(void* from, void* to)
{
setRel32(from, to);
}
-
+
static void repatchCompact(void* where, int32_t value)
{
ASSERT(value >= std::numeric_limits<int8_t>::min());
@@ -1925,7 +1925,7 @@ public:
{
setPointer(where, value);
}
-
+
static void* readPointer(void* where)
{
return reinterpret_cast<void**>(where)[-1];
@@ -1939,12 +1939,12 @@ public:
ptr[0] = static_cast<uint8_t>(OP_JMP_rel32);
*reinterpret_cast<int32_t*>(ptr + 1) = static_cast<int32_t>(distance);
}
-
+
static ptrdiff_t maxJumpReplacementSize()
{
return 5;
}
-
+
#if CPU(X86_64)
static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst)
{
@@ -1954,7 +1954,7 @@ public:
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
ptr[0] = PRE_REX | (1 << 3) | (dst >> 3);
ptr[1] = OP_MOV_EAXIv | (dst & 7);
-
+
union {
uint64_t asWord;
uint8_t asBytes[8];
@@ -1964,7 +1964,7 @@ public:
ptr[i] = u.asBytes[i - rexBytes - opcodeBytes];
}
#endif
-
+
static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst)
{
const int opcodeBytes = 1;
@@ -1981,7 +1981,7 @@ public:
for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i)
ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes];
}
-
+
static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst)
{
ASSERT_UNUSED(offset, !offset);
@@ -1999,7 +1999,7 @@ public:
for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i)
ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes];
}
-
+
static void replaceWithLoad(void* instructionStart)
{
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
@@ -2017,7 +2017,7 @@ public:
RELEASE_ASSERT_NOT_REACHED();
}
}
-
+
static void replaceWithAddressComputation(void* instructionStart)
{
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
@@ -2035,7 +2035,7 @@ public:
RELEASE_ASSERT_NOT_REACHED();
}
}
-
+
static unsigned getCallReturnOffset(AssemblerLabel call)
{
ASSERT(call.isSet());
@@ -2047,12 +2047,12 @@ public:
ASSERT(label.isSet());
return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + label.m_offset);
}
-
+
static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b)
{
return b.m_offset - a.m_offset;
}
-
+
PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort)
{
return m_formatter.executableCopy(globalData, ownerUID, effort);
@@ -2086,7 +2086,7 @@ private:
T *ptr = &reinterpret_cast<T*>(where)[idx];
memcpy(ptr, &value, sizeof(T));
}
-
+
static void setInt8(void* where, int8_t value)
{
reinterpret_cast<int8_t*>(where)[-1] = value;
@@ -2172,7 +2172,7 @@ private:
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, base, offset);
}
-
+
void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
m_buffer.ensureSpace(maxInstructionSize);
@@ -2286,7 +2286,7 @@ private:
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, base, offset);
}
-
+
void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
m_buffer.ensureSpace(maxInstructionSize);
@@ -2365,7 +2365,7 @@ private:
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm)
{
m_buffer.ensureSpace(maxInstructionSize);
- emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
+ emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
registerModRM(reg, rm);
@@ -2567,7 +2567,7 @@ private:
m_buffer.putIntUnchecked(offset);
}
}
-
+
void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset)
{
ASSERT(index != noIndex);
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index cee6b40599..f7d8a0b30f 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -97,6 +97,9 @@ static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable)
{
#if OS(QNX)
+ UNUSED_PARAM(usage);
+ UNUSED_PARAM(writable);
+ UNUSED_PARAM(executable);
// Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now.
void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
if (result == MAP_FAILED)
diff --git a/src/imports/builtins/CMakeLists.txt b/src/imports/builtins/CMakeLists.txt
index fb6984a961..e1632e1b09 100644
--- a/src/imports/builtins/CMakeLists.txt
+++ b/src/imports/builtins/CMakeLists.txt
@@ -4,3 +4,10 @@ qt_path_join(qml_install_dir "${QT_INSTALL_DIR}" "${INSTALL_QMLDIR}")
qt_copy_or_install(FILES ${qml_type_files}
DESTINATION ${qml_install_dir}
)
+
+# in prefix builds we also need to copy the files into the build directory of
+# the module, so that they are located together with the QML modules
+if(QT_WILL_INSTALL)
+ qt_path_join(qml_build_dir "${QT_BUILD_DIR}" "${INSTALL_QMLDIR}")
+ file(COPY ${qml_type_files} DESTINATION ${qml_build_dir})
+endif()
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index 93f6019901..4be2b6c526 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -23,6 +23,17 @@ Module {
}
Component {
+ name: "QVariantList"
+ valueType: "QVariant"
+ accessSemantics: "sequence"
+ }
+
+ Component {
+ name: "QVariantMap"
+ accessSemantics: "value"
+ }
+
+ Component {
name: "QJSValue"
accessSemantics: "value"
}
@@ -173,6 +184,12 @@ Module {
}
Component {
+ name: "QStringList"
+ valueType: "QString"
+ accessSemantics: "sequence"
+ }
+
+ Component {
name: "bool"
extension: "Boolean"
exports: ["QML/bool 1.0"]
@@ -197,17 +214,6 @@ Module {
}
Component {
- name: "QVariantList"
- valueType: "QVariant"
- accessSemantics: "sequence"
- }
-
- Component {
- name: "QVariantMap"
- accessSemantics: "value"
- }
-
- Component {
name: "QRegularExpression"
exports: ["QML/regexp 1.0"]
extension: "RegExp"
diff --git a/src/labs/models/qqmltablemodel_p.h b/src/labs/models/qqmltablemodel_p.h
index 60897fe007..c865d0b16a 100644
--- a/src/labs/models/qqmltablemodel_p.h
+++ b/src/labs/models/qqmltablemodel_p.h
@@ -55,6 +55,7 @@
#include "qqmltablemodelcolumn_p.h"
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtCore/QAbstractTableModel>
#include <QtQml/qqml.h>
#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
diff --git a/src/labs/models/qqmltablemodelcolumn_p.h b/src/labs/models/qqmltablemodelcolumn_p.h
index 7da31c9e2a..ba625bd656 100644
--- a/src/labs/models/qqmltablemodelcolumn_p.h
+++ b/src/labs/models/qqmltablemodelcolumn_p.h
@@ -54,6 +54,7 @@
#include "qqmlmodelsglobal_p.h"
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtQml/qqml.h>
#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
#include <QtQml/qjsvalue.h>
diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h
index 4e6089c814..6caa719e71 100644
--- a/src/particles/qquickparticleaffector_p.h
+++ b/src/particles/qquickparticleaffector_p.h
@@ -52,6 +52,7 @@
//
#include <QObject>
+#include <QSet>
#include "qquickparticlesystem_p.h"
#include "qquickparticleextruder_p.h"
#include "qtquickparticlesglobal_p.h"
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index 660794d454..b5a7889b7c 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -54,7 +54,9 @@
#include <QtQuick/QQuickItem>
#include <QElapsedTimer>
#include <QVector>
+#include <QVarLengthArray>
#include <QHash>
+#include <QSet>
#include <QPointer>
#include <private/qquicksprite_p.h>
#include <QAbstractAnimation>
diff --git a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
index 0c514ea6c3..7ebbf09dde 100644
--- a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
+++ b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
@@ -34,6 +34,6 @@ qt_internal_extend_target(QQmlPreviewServiceFactoryPlugin CONDITION QT_FEATURE_t
SOURCES
proxytranslator.cpp proxytranslator.h
qqmldebugtranslationservice.cpp qqmldebugtranslationservice.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
)
diff --git a/src/plugins/scenegraph/openvg/CMakeLists.txt b/src/plugins/scenegraph/openvg/CMakeLists.txt
index da1cb42656..a0999213bd 100644
--- a/src/plugins/scenegraph/openvg/CMakeLists.txt
+++ b/src/plugins/scenegraph/openvg/CMakeLists.txt
@@ -29,7 +29,7 @@ qt_internal_add_plugin(QSGOpenVGAdaptationPlugin
qsgopenvgrenderer.cpp qsgopenvgrenderer_p.h
qsgopenvgrenderloop.cpp qsgopenvgrenderloop_p.h
qsgopenvgtexture.cpp qsgopenvgtexture.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::Gui
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index 2fbf187978..6420411427 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -584,11 +584,6 @@ qt_internal_extend_target(Qml CONDITION hpux-_x_ OR solaris-_x_ OR (QT_FEATURE_c
rt
)
-qt_internal_extend_target(Qml CONDITION QT_FEATURE_qml_itemmodel
- SOURCES
- types/qqmlmodelindexvaluetype.cpp types/qqmlmodelindexvaluetype_p.h
-)
-
qt_internal_extend_target(Qml CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
WTF_USE_UDIS86=1
diff --git a/src/qml/Qt6AndroidQmlMacros.cmake b/src/qml/Qt6AndroidQmlMacros.cmake
index 9ccef4c8e3..df25285462 100644
--- a/src/qml/Qt6AndroidQmlMacros.cmake
+++ b/src/qml/Qt6AndroidQmlMacros.cmake
@@ -46,13 +46,8 @@ function(_qt_internal_generate_android_qml_deployment_settings out_var target)
${target} "_qt_android_native_qml_root_paths")
# Override qmlimportscanner binary path
- set(qml_importscanner_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}/qmlimportscanner")
- if (WIN32)
- string(APPEND qml_importscanner_binary_path ".exe")
- endif()
- file(TO_CMAKE_PATH "${qml_importscanner_binary_path}" qml_importscanner_binary_path_native)
- string(APPEND ${out_var}
- " \"qml-importscanner-binary\" : \"${qml_importscanner_binary_path_native}\",\n")
+ _qt_internal_add_tool_to_android_deployment_settings(${out_var} qmlimportscanner
+ "qml-importscanner-binary" ${target})
set(${out_var} "${${out_var}}" PARENT_SCOPE)
endfunction()
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index 87daf8688f..ad42596931 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -36,6 +36,7 @@ macro(qt_internal_get_internal_add_qml_module_keywords
IMPORTS
IMPORT_PATH
OPTIONAL_IMPORTS
+ DEFAULT_IMPORTS
DEPENDENCIES
PAST_MAJOR_VERSIONS
)
diff --git a/src/qml/Qt6QmlDeploySupport.cmake b/src/qml/Qt6QmlDeploySupport.cmake
index 58ca93b5fa..dc16261170 100644
--- a/src/qml/Qt6QmlDeploySupport.cmake
+++ b/src/qml/Qt6QmlDeploySupport.cmake
@@ -50,7 +50,9 @@ function(qt_deploy_qml_imports)
message(FATAL_ERROR
"No QML imports information recorded for target ${arg_TARGET}. "
"The target must be an executable and qt_add_qml_module() must "
- "have been called with it. Missing file:\n ${filename}"
+ "have been called with it. If using a CMake version lower than 3.19, ensure "
+ "that the executable is manually finalized with qt_finalize_target(). "
+ "Missing file:\n ${filename}"
)
endif()
include(${filename})
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 769eb87093..93ce173646 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -66,6 +66,7 @@ function(qt6_add_qml_module target)
IMPORTS
IMPORT_PATH
OPTIONAL_IMPORTS
+ DEFAULT_IMPORTS
DEPENDENCIES
PAST_MAJOR_VERSIONS
)
@@ -382,7 +383,7 @@ function(qt6_add_qml_module target)
set(arg_TYPEINFO ${target}.qmltypes)
endif()
- foreach(import_set IN ITEMS IMPORTS OPTIONAL_IMPORTS)
+ foreach(import_set IN ITEMS IMPORTS OPTIONAL_IMPORTS DEFAULT_IMPORTS)
foreach(import IN LISTS arg_${import_set})
string(FIND ${import} "/" slash_position REVERSE)
if (slash_position EQUAL -1)
@@ -448,7 +449,7 @@ function(qt6_add_qml_module target)
QT_QML_MODULE_NO_PLUGIN "${arg_NO_PLUGIN}"
QT_QML_MODULE_NO_PLUGIN_OPTIONAL "${arg_NO_PLUGIN_OPTIONAL}"
QT_QML_MODULE_NO_IMPORT_SCAN "${arg_NO_IMPORT_SCAN}"
- QT_QML_MODULE_FOLLOW_FOREIGN_VERSIONING "${arg_FOLLOW_FOREIGN_VERSIONING}"
+ _qt_qml_module_follow_foreign_versioning "${arg_FOLLOW_FOREIGN_VERSIONING}"
QT_QML_MODULE_URI "${arg_URI}"
QT_QML_MODULE_TARGET_PATH "${arg_TARGET_PATH}"
QT_QML_MODULE_VERSION "${arg_VERSION}"
@@ -689,6 +690,20 @@ macro(_qt_internal_genex_getoption var target property)
set(${var} "$<BOOL:$<TARGET_PROPERTY:${target},${property}>>")
endmacro()
+function(_qt_internal_extend_qml_import_paths import_paths_var)
+ set(local_var ${${import_paths_var}})
+
+ # prepend extra import path which is a current module's build dir: we need
+ # this to ensure correct importing of QML modules when having a prefix-build
+ # with QLibraryInfo::path(QLibraryInfo::QmlImportsPath) pointing to the
+ # install location
+ if(QT_BUILDING_QT AND QT_WILL_INSTALL)
+ list(PREPEND local_var -I "${QT_BUILD_DIR}/${INSTALL_QMLDIR}")
+ endif()
+
+ set(${import_paths_var} ${local_var} PARENT_SCOPE)
+endfunction()
+
function(_qt_internal_target_enable_qmllint target)
set(lint_target ${target}_qmllint)
if(TARGET ${lint_target})
@@ -737,6 +752,8 @@ function(_qt_internal_target_enable_qmllint target)
list(APPEND import_args -I "${QT_QML_OUTPUT_DIRECTORY}")
endif()
+ _qt_internal_extend_qml_import_paths(import_args)
+
set(cmd
${QT_TOOL_COMMAND_WRAPPER_PATH}
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
@@ -986,6 +1003,8 @@ function(_qt_internal_target_generate_qmldir target)
_qt_internal_qmldir_item_list(import QT_QML_MODULE_IMPORTS)
_qt_internal_qmldir_item_list("optional import" QT_QML_MODULE_OPTIONAL_IMPORTS)
+ _qt_internal_qmldir_item_list("default import" QT_QML_MODULE_DEFAULT_IMPORTS)
+
_qt_internal_qmldir_item_list(depends QT_QML_MODULE_DEPENDENCIES)
get_target_property(prefix ${target} QT_QML_MODULE_RESOURCE_PREFIX)
@@ -1109,6 +1128,8 @@ function(qt6_target_compile_qml_to_cpp target)
list(APPEND common_args -I "${import_path}")
endforeach()
+ _qt_internal_extend_qml_import_paths(common_args)
+
# we explicitly depend on qmldir (due to `-i ${qmldir_file}`) but also
# implicitly on the generated qmltypes file, which is a part of qmldir
set(qml_module_files)
@@ -1587,7 +1608,6 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
-
function(qt6_target_qml_sources target)
get_target_property(uri ${target} QT_QML_MODULE_URI)
@@ -1721,6 +1741,7 @@ function(qt6_target_qml_sources target)
# The application binary directory is part of the default import path.
list(APPEND import_paths -I "$<TARGET_PROPERTY:${target},BINARY_DIR>")
endif()
+ _qt_internal_extend_qml_import_paths(import_paths)
set(cachegen_args
${import_paths}
-i "${qmldir_file}"
@@ -1841,7 +1862,7 @@ function(qt6_target_qml_sources target)
# The set of qml files to run qmllint on may be a subset of the
# full set of files, so record these in a separate property.
_qt_internal_target_enable_qmllint(${target})
- set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${qml_file_src})
+ set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${file_absolute})
endif()
# Add qml file's type to qmldir
@@ -2061,22 +2082,24 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
-function(qt6_generate_foreign_qml_types lib_target qml_target)
-
- qt6_extract_metatypes(${lib_target})
- get_target_property(target_metatypes_json_file ${lib_target} INTERFACE_QT_META_TYPES_BUILD_FILE)
+# This function is currently in Technical Preview.
+# It's signature and behavior might change.
+function(qt6_generate_foreign_qml_types source_target destination_qml_target)
+ qt6_extract_metatypes(${source_target})
+ get_target_property(target_metatypes_json_file ${source_target}
+ INTERFACE_QT_META_TYPES_BUILD_FILE)
if (NOT target_metatypes_json_file)
message(FATAL_ERROR "Need target metatypes.json file")
endif()
- set(registration_files_base ${lib_target}_${qml_target})
+ set(registration_files_base ${source_target}_${destination_qml_target})
set(additional_sources ${registration_files_base}.cpp ${registration_files_base}.h)
add_custom_command(
OUTPUT
${additional_sources}
DEPENDS
- ${target}
+ ${source_target}
${target_metatypes_json_file}
${QT_CMAKE_EXPORT_NAMESPACE}::qmltyperegistrar
COMMAND
@@ -2085,14 +2108,13 @@ function(qt6_generate_foreign_qml_types lib_target qml_target)
"--extract"
-o ${registration_files_base}
${target_metatypes_json_file}
- COMMENT "Generate QML registration code for target ${target}"
+ COMMENT "Generate QML registration code for target ${source_target}"
)
- target_sources(${qml_target} PRIVATE ${additional_sources})
- qt6_wrap_cpp(${additional_sources} TARGET ${qml_target})
+ target_sources(${destination_qml_target} PRIVATE ${additional_sources})
+ qt6_wrap_cpp(${additional_sources} TARGET ${destination_qml_target})
endfunction()
-
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
function(qt_generate_foreign_qml_types)
@@ -2202,7 +2224,8 @@ function(_qt_internal_qml_type_registration target)
# Add --follow-foreign-versioning if requested
- get_target_property(follow_foreign_versioning ${target} QT_QML_MODULE_FOLLOW_FOREIGN_VERSIONING)
+ get_target_property(follow_foreign_versioning ${target}
+ _qt_qml_module_follow_foreign_versioning)
if (follow_foreign_versioning)
list(APPEND cmd_args
@@ -2313,10 +2336,12 @@ function(_qt_internal_qml_type_registration target)
endif()
add_dependencies(all_qmltyperegistrations ${target}_qmltyperegistration)
+ # Both ${target} (via target_sources) and ${target}_qmltyperegistration (via add_custom_target
+ # DEPENDS option) depend on ${type_registration_cpp_file}.
# The new Xcode build system requires a common target to drive the generation of files,
# otherwise project configuration fails.
- # Make the ${target}_qmltyperegistration the common target, by adding it as a dependency for
- # ${target} itself.
+ # Make ${target} the common target, by adding it as a dependency for
+ # ${target}_qmltyperegistration.
# The consequence is that the ${target}_qmllint target will now first build ${target} when using
# the Xcode generator (mostly only relevant for projects using Qt for iOS).
# See QTBUG-95763.
@@ -2721,6 +2746,22 @@ function(qt6_generate_deploy_qml_app_script)
message(FATAL_ERROR "FILENAME_VARIABLE must be specified")
endif()
+ # Check that the target was defer-finalized, and not immediately finalized when using
+ # CMake < 3.19. This is important because if it's immediately finalized, Qt::Qml is likely
+ # not in the dependency list, and thus _qt_internal_generate_deploy_qml_imports_script will
+ # not be executed, leading to an error at install time
+ # 'No QML imports information recorded for target X'.
+ # _qt_is_immediately_finalized is set by qt6_add_executable.
+ # TODO: Remove once minimum required CMAKE_VERSION is 3.19+.
+ get_target_property(is_immediately_finalized "${arg_TARGET}" _qt_is_immediately_finalized)
+ if(is_immediately_finalized)
+ message(FATAL_ERROR
+ "QML app deployment requires CMake version 3.19, or later, or manual executable "
+ "finalization. For manual finalization, pass the MANUAL_FINALIZATION option to "
+ "qt_add_executable() and then call qt_finalize_target(${arg_TARGET}) just before
+ calling qt_generate_deploy_qml_app_script().")
+ endif()
+
# Create a file name that will be unique for this target and the combination
# of arguments passed to this command. This allows the project to call us
# multiple times with different arguments for the same target (e.g. to
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 146588b750..2ed7c24efe 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -67,7 +67,6 @@
#include <private/qendian_p.h>
#include <private/qv4staticvalue_p.h>
-#include <private/qml_compile_hash_p.h>
#include <functional>
#include <limits.h>
@@ -945,7 +944,6 @@ struct QmlUnit
static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
enum { QmlCompileHashSpace = 48 };
-static_assert(QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
static const char magic_str[] = "qv4cdata";
struct Unit
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 59998e531c..4813e2203c 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -56,6 +56,9 @@
#include <QtCore/QDateTime>
#include <QtCore/QStack>
#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QSet>
+#include <QtCore/QVarLengthArray>
#include <memory>
diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h
index d0ceb28cdc..d2a0570423 100644
--- a/src/qml/debugger/qqmldebug.h
+++ b/src/qml/debugger/qqmldebug.h
@@ -43,6 +43,7 @@
#include <QtQml/qtqmlglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
+#include <QtCore/qhash.h> // QVariantHash
QT_BEGIN_NAMESPACE
diff --git a/src/qml/doc/snippets/code/doc_src_qtqml.cmake b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
index c312201ff8..30ff8981a9 100644
--- a/src/qml/doc/snippets/code/doc_src_qtqml.cmake
+++ b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
@@ -2,3 +2,9 @@
find_package(Qt6 REQUIRED COMPONENTS Qml)
target_link_libraries(mytarget PRIVATE Qt6::Qml)
#! [0]
+
+#! [1]
+find_package(Qt6 REQUIRED COMPONENTS QmlIntegration)
+target_link_libraries(mytarget PRIVATE Qt6::QmlIntegration)
+#! [1]
+
diff --git a/src/qml/doc/snippets/qmltc/CMakeLists.txt b/src/qml/doc/snippets/qmltc/CMakeLists.txt
index 69bfda7249..671db0e30d 100644
--- a/src/qml/doc/snippets/qmltc/CMakeLists.txt
+++ b/src/qml/doc/snippets/qmltc/CMakeLists.txt
@@ -52,8 +52,8 @@ qt6_add_qml_module(${application_name}
# (qmltc-specific) Link *private* libraries that correspond to QML modules:
target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivate)
-# Compile qml files (listed in FILES) to C++ using qmltc and add these files to
-# the application binary:
+# Compile qml files (listed in QML_FILES) to C++ using qmltc and add these files
+# to the application binary:
qt6_target_compile_qml_to_cpp(${application_name}
QML_FILES ${application_qml_files}
)
diff --git a/src/qml/doc/snippets/qmltc/myApp.qml b/src/qml/doc/snippets/qmltc/myApp.qml
index 234fa39ab9..80d94829bf 100644
--- a/src/qml/doc/snippets/qmltc/myApp.qml
+++ b/src/qml/doc/snippets/qmltc/myApp.qml
@@ -91,6 +91,7 @@ Rectangle {
id: rndText
font.pixelSize: 25
color: textColor
+ text: "0.00"
}
Rectangle {
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
index ddbf3baaaa..122084f1d8 100644
--- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
@@ -71,15 +71,15 @@ class HelloWorld : public QObject
public:
HelloWorld(QQmlEngine * engine, QObject * parent = nullptr);
+Q_SIGNALS:
+ void created();
+
public:
void setHello(const QString& hello_);
QString hello();
QBindable<QString> bindableHello();
Q_INVOKABLE void printHello(QString prefix, QString suffix);
-signals:
- void created();
-
// ...
};
//! [qmltc-hello-world-generated]
diff --git a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
index 50436dc26f..2d619ebeea 100644
--- a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
+++ b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
@@ -177,6 +177,9 @@ void readFileContent(QStringList *content, const QString &url, Predicate filter)
void tst_qmltc_examples::helloWorld()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("expected C++ files are not bundled with Android tests.");
+#endif
QStringList generatedCode;
readFileContent(&generatedCode,
QStringLiteral(QMLTC_TESTS_BINARY_DIR)
diff --git a/src/qml/doc/src/cmake/cmake-properties.qdoc b/src/qml/doc/src/cmake/cmake-properties.qdoc
new file mode 100644
index 0000000000..eccc815eca
--- /dev/null
+++ b/src/qml/doc/src/cmake/cmake-properties.qdoc
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\group cmake-source-file-properties-qtqml
+\title CMake Source File Properties in Qt6 Qml
+
+\l{CMake Commands in Qt6 Qml}{CMake Commands} know about the following CMake
+source file properties:
+
+\sa{CMake Property Reference}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_INTERNAL_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_INTERNAL_TYPE
+\target cmake-source-file-property-QT_QML_INTERNAL_TYPE
+
+\summary {Marks a QML file as providing an internal type.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to indicate that the \c{.qml} file provides an internal type.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SINGLETON_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SINGLETON_TYPE
+\target cmake-source-file-property-QT_QML_SINGLETON_TYPE
+
+\summary {Marks a QML file as providing a singleton type.}
+
+\cmakepropertysince 6.2
+
+A \c{.qml} file that provides a singleton type needs to have its \c QT_QML_SINGLETON_TYPE source
+property set to \c TRUE to ensure that the singleton command is written into the
+\l{Module Definition qmldir Files}{qmldir} file.
+This must be done in addition to the QML file containing the \c {pragma Singleton} statement.
+
+See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
+how to set the \c QT_QML_SINGLETON_TYPE property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_CACHEGEN.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_CACHEGEN
+\target cmake-source-file-property-QT_QML_SKIP_CACHEGEN
+
+\summary {Excludes a file from being compiled to byte code.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the \c{.qml} file from being compiled to byte code.
+The file will still be added to the \c target as a resource in uncompiled form
+(see \l{qmlcachegen-auto}{Caching compiled QML sources}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLDIR_ENTRY
+\target cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY
+
+\summary {Excludes a file from being added as a type to the QML module's typeinfo file.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent
+the \c{.qml} file from being added as a type to the QML module's typeinfo file
+(see \l{qmldir-autogeneration}{Auto-generating \c{qmldir} and typeinfo files}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLLINT.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLLINT
+\target cmake-source-file-property-QT_QML_SKIP_QMLLINT
+
+\summary {Prevents a file from being included in automatic qmllint processing.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the file from being included in
+\l{qmllint-auto}{automatic qmllint processing}.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_TYPENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_TYPENAME
+\target cmake-source-file-property-QT_QML_SOURCE_TYPENAME
+
+\summary {Overrides the type name provided by the file.}
+
+\cmakepropertysince 6.2
+
+Use this property to override the \c QML type name provided by this file.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_VERSIONS.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_VERSIONS
+\target cmake-source-file-property-QT_QML_SOURCE_VERSIONS
+
+\summary {Specifies a custom set of versions for a type.}
+
+\cmakepropertysince 6.2
+
+When the file needs to provide type entries for a custom set of versions,
+for example when the QML types were first introduced in a minor patch
+version after the \c{.0} release, specify those versions using this property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QMLTC_FILE_BASENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QMLTC_FILE_BASENAME
+\target cmake-source-file-property-QT_QMLTC_FILE_BASENAME
+
+\summary {Specifies a non-default .h and .cpp file name.}
+
+\cmakepropertysince 6.3
+\preliminarycmakeproperty
+
+Use this property to specify a non-default \c .h and \c .cpp file name, which helps to resolve
+conflicting file names.
+
+\sa{qt_target_compile_qml_to_cpp}
+*/
diff --git a/src/qml/doc/src/cmake/cmake-variables.qdoc b/src/qml/doc/src/cmake/cmake-variables.qdoc
index 8f5b87b1cb..df01534d66 100644
--- a/src/qml/doc/src/cmake/cmake-variables.qdoc
+++ b/src/qml/doc/src/cmake/cmake-variables.qdoc
@@ -27,7 +27,6 @@
/*!
\page cmake-variable-QT_QML_OUTPUT_DIRECTORY.html
-\ingroup cmake-variables
\ingroup cmake-variables-qtqml
\title QT_QML_OUTPUT_DIRECTORY
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index d357497e24..00381d086d 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -34,6 +34,8 @@
\brief Defines a QML module.
+\cmakecommandsince 6.2
+
\section1 Synopsis
\badcode
@@ -50,6 +52,7 @@ qt_add_qml_module(
[TYPEINFO typeinfo]
[IMPORTS ...]
[OPTIONAL_IMPORTS ...]
+ [DEFAULT_IMPORTS ...]
[DEPENDENCIES ...]
[IMPORT_PATH ...]
[SOURCES ...]
@@ -467,6 +470,15 @@ like \c qmllint. Versions can be specified in the same way as for \c IMPORTS.
Each module listed here will be added as an \c{optional import} entry in the
generated \l{Module Definition qmldir Files}{qmldir} file.
+\c DEFAULT_IMPORTS specifies which of the optional imports are the default entries
+that should be loaded by tooling. One entry should be specified for every group of
+\c OPTIONAL_IMPORTS in the module. As optional imports are only resolved at runtime,
+tooling like qmllint cannot in general know which of the optional imports should
+be resolved. To remedy this, you can specify one of the optional imports as the
+default import; tooling will then pick it. If you have one optional import that
+gets used at runtime without any further configuration, that is an ideal candidate
+for the default import.
+
\c DEPENDENCIES provides a list of other QML modules that this module depends
on, but doesn't necessarily import. It would typically be used for dependencies
that only exist at the C++ level, such as a module registering a class to QML
diff --git a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
index 0282e2347e..369e698c39 100644
--- a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
+++ b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
@@ -42,6 +42,8 @@ project.
\preliminarycmakecommand
+\include cmake-qml-qt-finalize-target-warning.cmake
+
\section1 Synopsis
\badcode
@@ -97,7 +99,7 @@ named by the \c{PLUGINS_FOUND} option, if given. This is often passed as the
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
qt_standard_project_setup()
qt_add_executable(MyApp main.cpp)
@@ -113,7 +115,23 @@ set(deploy_script "${CMAKE_CURRENT_BINARY_DIR}/deploy_MyApp.cmake")
file(GENERATE OUTPUT ${deploy_script} CONTENTS "
include(\"${QT_DEPLOY_SUPPORT}\")
qt_deploy_qml_imports(
- EXECUTABLE \"\${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:MyApp>\"
+ # Deploy QML modules used by MyApp
+ TARGET MyApp
+
+ # The found QML plugins are stored in the plugins_found variable
+ PLUGINS_FOUND plugins_found
+
+ # The QML modules will be deployed into a custom directory
+ QML_DIR \"myqmldir\"
+
+ # Qt QML modules will be skipped, only project-created QML modules will be deployed
+ NO_QT_IMPORTS
+)
+# Deploy application runtime dependencies and runtime dependencies
+# of the found QML module plugins.
+qt_deploy_runtime_dependencies(
+ EXECUTABLE \${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:MyApp>
+ ADDITIONAL_MODULES \${plugins_found}
)
")
diff --git a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
index 1fae4ef9d1..360d602f8c 100644
--- a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
@@ -36,8 +36,11 @@
\include cmake-find-package-qml.qdocinc
+\cmakecommandsince 6.3
\preliminarycmakecommand
+\include cmake-qml-qt-finalize-target-warning.cmake
+
\section1 Synopsis
\badcode
@@ -122,7 +125,7 @@ same target.
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
qt_standard_project_setup()
qt_add_executable(MyApp main.cpp)
diff --git a/src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
index 07cdd92543..8f1d6522be 100644
--- a/src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
@@ -26,20 +26,24 @@
****************************************************************************/
/*!
-\page qt6_generate_foreign_qml_types.html
+\page qt_generate_foreign_qml_types.html
\ingroup cmake-commands-qtqml
-\title qt6_generate_foreign_qml_types
+\title qt_generate_foreign_qml_types
\target qt6_generate_foreign_qml_types
\summary{Registers types from one target in a QML module.}
+\include cmake-find-package-qml.qdocinc
+
+\preliminarycmakecommand
+
\section1 Synopsis
\badcode
-qt_add_qml_module(
- foreign_target
- qml_lib_target
+qt_generate_foreign_qml_types(
+ source_target
+ destination_qml_target
)
\endcode
@@ -48,8 +52,9 @@ qt_add_qml_module(
\section1 Description
-\c qt6_generate_foreign_qml_types enables the registration of types marked via QML registration
-macros (like \l QML_ELEMENT) in \c foreign_lib in the QML module \c qml_lib_target.
+\c qt_generate_foreign_qml_types extracts types marked via QML registration
+macros (like \l QML_ELEMENT) from \c source_target and registers them as foreign
+types in the QML module \c destination_qml_target.
This can be useful when one wants to create a library with optional QML integration, without
depending directly on QML.
@@ -70,14 +75,14 @@ class MyClass : public QObject
\badcode
# CMakeLists.txt
qt_add_library(mylib myclass.h ...)
-target_link_libraries(mylib Qt::Core Qt::QmlIntegration)
+target_link_libraries(mylib PRIVATE Qt::Core Qt::QmlIntegration)
qt_add_qml_module(mylib_declarative
VERSION 1.0
URI "mylib"
...
)
-qt6_generate_foreign_qml_types(mylib mylib_declarative)
+qt_generate_foreign_qml_types(mylib mylib_declarative)
\endcode
\note In the example above, \c mylib does not depend on QtQml or QtQuick, but only on the
diff --git a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
index 639c7ad223..b720d6d592 100644
--- a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
+++ b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
@@ -34,6 +34,8 @@
\brief Ensures QML plugins needed by a target are imported for static builds.
+\cmakecommandsince 6.0
+
\section1 Synopsis
\badcode
diff --git a/src/qml/doc/src/cmake/qt_query_qml_module.qdoc b/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
index b2ab6c7c86..ec017ba3e7 100644
--- a/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
@@ -36,6 +36,7 @@
\include cmake-find-package-qml.qdocinc
+\cmakecommandsince 6.3
\preliminarycmakecommand
\section1 Synopsis
@@ -161,7 +162,7 @@ absolute. The meaning and usage of \c RESOURCES_DEPLOY_PATHS and
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
set(module_name "MyThings")
qt_add_qml_module(${module_name}
diff --git a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
index c5a7212caa..dc1178be29 100644
--- a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
+++ b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
@@ -34,6 +34,8 @@
\brief Add qml files and resources to an existing QML module target.
+\cmakecommandsince 6.2
+
\section1 Synopsis
\badcode
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index ad0d2243ec..fd29e733e0 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -271,9 +271,9 @@ Notice the parameter and return type specified after the colon. You can use \l
{QML Value Types}{value types} and \l {QML Object Types}{object types} as type
names.
-If the type is omitted in QML, then you must specify QVariant as type with
-Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod.
-
+If the type is omitted or specified as \c var in QML, then you must pass
+QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
+QMetaObject::invokeMethod.
\section2 Connecting to QML Signals
diff --git a/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake
new file mode 100644
index 0000000000..d3c7383d2e
--- /dev/null
+++ b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake
@@ -0,0 +1,4 @@
+\warning If you are using a CMake version lower than 3.19, make sure that you
+pass the \c MANUAL_FINALIZATION option to
+\l{qt_add_executable}{qt6_add_executable()}, and then call
+\l{qt_finalize_target}{qt6_finalize_target()} before calling this function.
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
index 53fe1c2875..375b8758d5 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
@@ -136,7 +136,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype int
+ \qmlvaluetype int
\ingroup qmlvaluetypes
\brief a whole number, e.g. 0, 10, or -20.
@@ -157,7 +157,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype bool
+ \qmlvaluetype bool
\ingroup qmlvaluetypes
\brief a binary true/false value.
@@ -177,7 +177,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype real
+ \qmlvaluetype real
\ingroup qmlvaluetypes
\brief a number with a decimal point.
@@ -199,7 +199,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype double
+ \qmlvaluetype double
\ingroup qmlvaluetypes
\brief a number with a decimal point, stored in double precision.
@@ -220,7 +220,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype string
+ \qmlvaluetype string
\ingroup qmlvaluetypes
\brief a free form text string.
@@ -247,7 +247,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype url
+ \qmlvaluetype url
\ingroup qmlvaluetypes
\brief a resource locator.
@@ -303,7 +303,7 @@ property is only invoked when the property is reassigned to a different object v
/*!
- \qmlbasictype list
+ \qmlvaluetype list
\ingroup qmlvaluetypes
\brief a list of QML objects.
@@ -385,7 +385,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype var
+ \qmlvaluetype var
\ingroup qmlvaluetypes
\brief a generic property type.
@@ -485,7 +485,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype enumeration
+ \qmlvaluetype enumeration
\ingroup qmlvaluetypes
\brief a named enumeration value.
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc
index 3efcc7cf53..b8decf6820 100644
--- a/src/qml/doc/src/qmltypereference.qdoc
+++ b/src/qml/doc/src/qmltypereference.qdoc
@@ -83,7 +83,7 @@ provided:
*/
/*!
-\qmlbasictype date
+\qmlvaluetype date
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a date value.
@@ -123,7 +123,7 @@ to a \l{QtQml::Date}{Date} object.
*/
/*!
-\qmlbasictype point
+\qmlvaluetype point
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with x and y attributes.
@@ -151,7 +151,7 @@ is automatically converted into a QPointF value.
*/
/*!
-\qmlbasictype size
+\qmlvaluetype size
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with width and height attributes
@@ -189,7 +189,7 @@ is automatically converted into a QSizeF value.
*/
/*!
-\qmlbasictype rect
+\qmlvaluetype rect
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with x, y, width and height attributes.
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index 4de3115511..de4fa16e29 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -69,6 +69,13 @@ Use the \c find_package() command to locate the needed module components in the
\snippet code/doc_src_qtqml.cmake 0
+To provide foreign QML type support for a non-QML library, locate
+the \c QmlIntegration module as follows:
+
+\snippet code/doc_src_qtqml.cmake 1
+
+See \l{qt6_generate_foreign_qml_types} for details.
+
See also the \l{Build with CMake} overview.
\section3 Building with qmake
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 35b1f18344..93b1e88a1b 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -55,6 +55,7 @@
#include <private/qv4function_p.h>
#include <private/qv4instr_moth_p.h>
#include <private/qv4bytecodehandler_p.h>
+#include <QtCore/qset.h>
#if QT_CONFIG(qml_jit)
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 7b436645e9..f66488e04c 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -861,73 +861,87 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
return convertV2(value, QMetaType(type), ptr);
}
+static bool convertString(const QString &string, QMetaType metaType, void *ptr)
+{
+ // have a string based value without engine. Do conversion manually
+ if (metaType == QMetaType::fromType<bool>()) {
+ *reinterpret_cast<bool*>(ptr) = string.length() != 0;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QString>()) {
+ *reinterpret_cast<QString*>(ptr) = string;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QUrl>()) {
+ *reinterpret_cast<QUrl *>(ptr) = QUrl(string);
+ return true;
+ }
+
+ double d = QV4::RuntimeHelpers::stringToNumber(string);
+ switch (metaType.id()) {
+ case QMetaType::Int:
+ *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UInt:
+ *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::LongLong:
+ *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::ULongLong:
+ *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::Double:
+ *reinterpret_cast<double*>(ptr) = d;
+ return true;
+ case QMetaType::Float:
+ *reinterpret_cast<float*>(ptr) = d;
+ return true;
+ case QMetaType::Short:
+ *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UShort:
+ *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::Char:
+ *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UChar:
+ *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::QChar:
+ *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
+ return true;
+ case QMetaType::Char16:
+ *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ default:
+ return false;
+ }
+}
+
/*!
\internal
convert \a value to \a type, store the result in \a ptr
*/
bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
{
- if (const QString *string = QJSValuePrivate::asQString(&value)) {
- // have a string based value without engine. Do conversion manually
- if (metaType == QMetaType::fromType<bool>()) {
- *reinterpret_cast<bool*>(ptr) = string->length() != 0;
- return true;
- }
- if (metaType == QMetaType::fromType<QString>()) {
- *reinterpret_cast<QString*>(ptr) = *string;
- return true;
- }
- if (metaType == QMetaType::fromType<QUrl>()) {
- *reinterpret_cast<QUrl *>(ptr) = QUrl(*string);
- return true;
- }
-
- double d = QV4::RuntimeHelpers::stringToNumber(*string);
- switch (metaType.id()) {
- case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::Double:
- *reinterpret_cast<double*>(ptr) = d;
- return true;
- case QMetaType::Float:
- *reinterpret_cast<float*>(ptr) = d;
- return true;
- case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
- return true;
- case QMetaType::Char16:
- *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
- return true;
- default:
- return false;
- }
- }
+ if (const QString *string = QJSValuePrivate::asQString(&value))
+ return convertString(*string, metaType, ptr);
return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr);
}
+bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr)
+{
+ if (value.metaType() == QMetaType::fromType<QString>())
+ return convertString(value.toString(), metaType, ptr);
+
+ // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
+ // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
+ return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromVariant(value), metaType, ptr);
+}
+
/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
Creates a QJSValue with the given \a value.
@@ -944,6 +958,18 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
\sa toScriptValue()
*/
+/*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value)
+
+ Returns the given \a value converted to the template type \c{T}.
+ This works with any type \c{T} that has a \c{QMetaType}. The
+ conversion is done in JavaScript semantics. Those differ from
+ qvariant_cast's semantics. There are a number of implicit
+ conversions between JavaScript-equivalent types that are not
+ performed by qvariant_cast by default.
+
+ \sa fromScriptValue() qvariant_cast()
+*/
+
/*!
Throws a run-time error (exception) with the given \a message.
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 23578a63b6..63d6a6dd32 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -113,6 +113,33 @@ public:
return qjsvalue_cast<T>(value);
}
+ template <typename T>
+ inline T fromVariant(const QVariant &value)
+ {
+ if constexpr (std::is_same_v<T, QVariant>)
+ return value;
+
+ const QMetaType targetType = QMetaType::fromType<T>();
+ if (value.metaType() == targetType)
+ return *reinterpret_cast<const T *>(value.constData());
+
+ if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) {
+ using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
+ const QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
+ if (value.metaType() == nonConstTargetType)
+ return *reinterpret_cast<const nonConstT *>(value.constData());
+ }
+
+ {
+ T t{};
+ if (convertVariant(value, targetType, &t))
+ return t;
+
+ QMetaType::convert(value.metaType(), value.constData(), targetType, &t);
+ return t;
+ }
+ }
+
void collectGarbage();
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
@@ -157,6 +184,7 @@ private:
static bool convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr);
static bool convertV2(const QJSValue &value, int type, void *ptr);
static bool convertV2(const QJSValue &value, QMetaType metaType, void *ptr);
+ bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr);
template<typename T>
friend inline T qjsvalue_cast(const QJSValue &);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index aed640281f..033331e08e 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -147,14 +147,23 @@ DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
using namespace QV4;
+// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1
+// initializes the statics and sets engineSerial to 2 afterwards.
+// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays
+// odd while the statics are being initialized, and stays even afterwards.
+// Any further engines created while the statics are being initialized busy-wait until engineSerial
+// is even.
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+int ExecutionEngine::s_maxCallDepth = -1;
+int ExecutionEngine::s_jitCallCountThreshold = 3;
+int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
+int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
{
return b->engine()->throwTypeError();
}
-qint32 ExecutionEngine::maxCallDepth = -1;
template <typename ReturnType>
ReturnType convertJSValueToVariantType(const QJSValue &value)
@@ -324,6 +333,59 @@ static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
return QSequentialIterable(QMetaSequence(&sequence), &value);
}
+void ExecutionEngine::initializeStaticMembers()
+{
+ bool ok = false;
+
+ const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxJSStackSize > 0)
+ s_maxJSStackSize = envMaxJSStackSize;
+
+ const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxGCStackSize > 0)
+ s_maxGCStackSize = envMaxGCStackSize;
+
+ if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
+ s_maxCallDepth = std::numeric_limits<qint32>::max();
+ } else {
+ ok = false;
+ s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
+ if (!ok || s_maxCallDepth <= 0) {
+#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
+#ifdef Q_OS_QNX
+ s_maxCallDepth = 640; // QNX's stack is only 512k by default
+#else
+ s_maxCallDepth = 1234;
+#endif
+#else
+ // no (tail call) optimization is done, so there'll be a lot mare stack frames active
+ s_maxCallDepth = 200;
+#endif
+ }
+ }
+
+ Q_ASSERT(s_maxCallDepth > 0);
+
+ ok = false;
+ s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
+ if (!ok)
+ s_jitCallCountThreshold = 3;
+ if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
+ s_jitCallCountThreshold = std::numeric_limits<int>::max();
+
+ qMetaTypeId<QJSValue>();
+ qMetaTypeId<QList<int> >();
+
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
+ QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
+ QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
+ QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
+ QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -332,7 +394,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, publicEngine(jsEngine)
- , m_engineId(engineSerial.fetchAndAddOrdered(1))
+ , m_engineId(engineSerial.fetchAndAddOrdered(2))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
#if QT_CONFIG(qml_jit)
@@ -343,45 +405,23 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
#endif
, m_qmlEngine(nullptr)
{
- bool ok = false;
- const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
- if (ok && envMaxJSStackSize > 0)
- m_maxJSStackSize = envMaxJSStackSize;
-
- const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
- if (ok && envMaxGCStackSize > 0)
- m_maxGCStackSize = envMaxGCStackSize;
-
- memoryManager = new QV4::MemoryManager(this);
-
- if (maxCallDepth == -1) {
- if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
- maxCallDepth = std::numeric_limits<qint32>::max();
- } else {
- ok = false;
- maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
- if (!ok || maxCallDepth <= 0) {
-#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
-#ifdef Q_OS_QNX
- maxCallDepth = 640; // QNX's stack is only 512k by default
-#else
- maxCallDepth = 1234;
-#endif
-#else
- // no (tail call) optimization is done, so there'll be a lot mare stack frames active
- maxCallDepth = 200;
-#endif
- }
+ if (m_engineId == 1) {
+ initializeStaticMembers();
+ engineSerial.storeRelease(2); // make it even
+ } else if (Q_UNLIKELY(m_engineId & 1)) {
+ // This should be rare. You usually don't create lots of engines at the same time.
+ while (engineSerial.loadAcquire() & 1) {
+ QThread::yieldCurrentThread();
}
}
- Q_ASSERT(maxCallDepth > 0);
+ memoryManager = new QV4::MemoryManager(this);
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
- *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
jsStackBase = (Value *)jsStack->base();
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
@@ -389,19 +429,10 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
+ *gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
/* writable */ true, /* executable */ false,
/* includesGuardPages */ true);
- {
- ok = false;
- jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
- if (!ok)
- jitCallCountThreshold = 3;
- if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
- jitCallCountThreshold = std::numeric_limits<int>::max();
- }
-
exceptionValue = jsAlloca(1);
*exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
@@ -412,7 +443,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
- jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value);
+ jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
identifierTable = new IdentifierTable(this);
@@ -855,18 +886,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
- qMetaTypeId<QJSValue>();
- qMetaTypeId<QList<int> >();
-
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
- QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
- QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
- QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
- QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
-
QV4::QObjectWrapper::initializeBindings(this);
m_delayedCallQueue.init(this);
@@ -1978,12 +1997,12 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *dat
int ExecutionEngine::maxJSStackSize() const
{
- return m_maxJSStackSize;
+ return s_maxJSStackSize;
}
int ExecutionEngine::maxGCStackSize() const
{
- return m_maxGCStackSize;
+ return s_maxGCStackSize;
}
ReturnedValue ExecutionEngine::global()
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 617c7361cf..b9f2633d91 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -60,6 +60,7 @@
#include <private/qqmldelayedcallqueue_p.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qset.h>
#include "qv4function_p.h"
#include <private/qv4compileddata_p.h>
@@ -169,8 +170,6 @@ class ReactionHandler;
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
{
private:
- static qint32 maxCallDepth;
-
friend struct ExecutionContextSaver;
friend struct ExecutionContext;
friend struct Heap::ExecutionContext;
@@ -695,7 +694,7 @@ public:
return false;
if (f) {
return !f->aotFunction && !f->isGenerator()
- && f->interpreterCallCount >= jitCallCountThreshold;
+ && f->interpreterCallCount >= s_jitCallCountThreshold;
}
return true;
#else
@@ -772,13 +771,18 @@ public:
private:
QV4::ReturnedValue fromData(QMetaType type, const void *ptr, const QVariant *variant = nullptr);
+ static void initializeStaticMembers();
+
+ static int s_maxCallDepth;
+ static int s_jitCallCountThreshold;
+ static int s_maxJSStackSize;
+ static int s_maxGCStackSize;
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
#endif
QSet<QString> m_illegalNames;
- int jitCallCountThreshold;
// used by generated Promise objects to handle 'then' events
QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler;
@@ -797,9 +801,6 @@ private:
QHash<QString, quint32> m_consoleCount;
QVector<Deletable *> m_extensionData;
-
- int m_maxJSStackSize = 4 * 1024 * 1024;
- int m_maxGCStackSize = 2 * 1024 * 1024;
};
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
@@ -815,7 +816,7 @@ struct ExecutionEngineCallDepthRecorder
inline bool ExecutionEngine::checkStackLimits()
{
- if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
+ if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= s_maxCallDepth))) {
throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
return true;
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 1a5e76cbc9..6e0d07210c 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -69,6 +69,8 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/QScopedValueRollback>
+static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
+
#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
# ifdef Q_OS_LINUX
// Place on a separate section on Linux so it's easier to check from outside
@@ -856,7 +858,8 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
This function creates a temporary key vector and sorts it to guarantuee a stable
hash. This is used to calculate a check-sum on dependent meta-objects.
*/
-bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash) const
+bool ResolvedTypeReferenceMap::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
{
std::vector<int> keys (count());
int i = 0;
@@ -866,7 +869,7 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash) const
}
std::sort(keys.begin(), keys.end());
for (int key: keys) {
- if (!this->operator[](key)->addToHash(hash))
+ if (!this->operator[](key)->addToHash(hash, checksums))
return false;
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index 6798829898..c093033aab 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -94,7 +94,7 @@ class ResolvedTypeReference;
// map from name index
struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
{
- bool addToHash(QCryptographicHash *hash) const;
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
};
class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit,
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index ebce416172..d964fadd18 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -131,6 +131,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
QQmlPropertyData *propertyData;
} qobjectLookup;
struct {
+ quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc
+ quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
+ int coreIndex;
+ int notifyIndex;
+ } qobjectFallbackLookup;
+ struct {
Heap::InternalClass *ic;
quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index c9a9ab138a..f7a5f897c8 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -640,6 +640,11 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
+ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ return resolveQmlContextPropertyLookupGetter(l, engine, base);
+}
+
ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index cbbd04bff0..1da64b101f 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp
index 96bddccf83..4ac68611f4 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference.cpp
+++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp
@@ -100,11 +100,12 @@ QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache()
}
}
-bool ResolvedTypeReference::addToHash(QCryptographicHash *hash)
+bool ResolvedTypeReference::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums)
{
if (m_type.isValid() && !m_type.isInlineComponentType()) {
bool ok = false;
- hash->addData(createPropertyCache()->checksum(&ok));
+ hash->addData(createPropertyCache()->checksum(checksums, &ok));
return ok;
}
if (!m_compilationUnit)
diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h
index 61e683ce17..582c3a5d0a 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference_p.h
+++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h
@@ -75,7 +75,7 @@ public:
QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
QQmlRefPointer<QQmlPropertyCache> createPropertyCache();
- bool addToHash(QCryptographicHash *hash);
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums);
void doDynamicTypeCheck();
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 42ad6b8716..a265a67e24 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -129,8 +129,14 @@ public:
QVariant at(int index) const
{
const auto *p = d();
- QVariant result(valueMetaType(p));
- metaSequence(p)->valueAtIndex(p->container, index, result.data());
+ const QMetaType v = valueMetaType(p);
+ QVariant result;
+ if (v == QMetaType::fromType<QVariant>()) {
+ metaSequence(p)->valueAtIndex(p->container, index, &result);
+ } else {
+ result = QVariant(v);
+ metaSequence(p)->valueAtIndex(p->container, index, result.data());
+ }
return result;
}
@@ -141,6 +147,8 @@ public:
const QMetaType v = valueMetaType(p);
if (item.metaType() == v) {
m->addValueAtEnd(p->container, item.constData());
+ } else if (v == QMetaType::fromType<QVariant>()) {
+ m->addValueAtEnd(p->container, &item);
} else {
QVariant converted = item;
if (!converted.convert(v))
@@ -156,6 +164,8 @@ public:
const QMetaType v = valueMetaType(p);
if (item.metaType() == v) {
m->setValueAtIndex(p->container, index, item.constData());
+ } else if (v == QMetaType::fromType<QVariant>()) {
+ m->setValueAtIndex(p->container, index, &item);
} else {
QVariant converted = item;
if (!converted.convert(v))
@@ -270,8 +280,11 @@ public:
} else {
/* according to ECMA262r3 we need to insert */
/* the value at the given index, increasing length to index+1. */
- while (index > count++)
- append(QVariant(valueType));
+ while (index > count++) {
+ append(valueType == QMetaType::fromType<QVariant>()
+ ? QVariant()
+ : QVariant(valueType));
+ }
append(element);
}
@@ -687,15 +700,19 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
for (quint32 i = 0; i < length; ++i) {
const QMetaType valueMetaType = priv->typeId;
QVariant variant = scope.engine->toVariant(a->get(i), valueMetaType, false);
- const QMetaType originalType = variant.metaType();
- if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
- qWarning() << QLatin1String(
- "Could not convert array value at position %1 from %2 to %3")
- .arg(QString::number(i), QString::fromUtf8(originalType.name()),
- QString::fromUtf8(valueMetaType.name()));
- variant = QVariant(valueMetaType);
+ if (valueMetaType == QMetaType::fromType<QVariant>()) {
+ meta->addValueAtEnd(result.data(), &variant);
+ } else {
+ const QMetaType originalType = variant.metaType();
+ if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
+ qWarning() << QLatin1String(
+ "Could not convert array value at position %1 from %2 to %3")
+ .arg(QString::number(i), QString::fromUtf8(originalType.name()),
+ QString::fromUtf8(valueMetaType.name()));
+ variant = QVariant(valueMetaType);
+ }
+ meta->addValueAtEnd(result.data(), variant.constData());
}
- meta->addValueAtEnd(result.data(), variant.constData());
}
return result;
}
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 593a6696fb..ff09ec815c 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -61,8 +61,8 @@
#include "qv4string_p.h"
#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qitemselectionmodel.h>
#endif
QT_BEGIN_NAMESPACE
@@ -110,16 +110,13 @@ QT_DECLARE_SEQUENTIAL_CONTAINER(QRealStdVectorForeign, std::vector<qreal>, doubl
QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, double);
#endif
+QT_DECLARE_SEQUENTIAL_CONTAINER(QDoubleStdVectorForeign, std::vector<double>, double);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QFloatStdVectorForeign, std::vector<float>, float);
QT_DECLARE_SEQUENTIAL_CONTAINER(QIntStdVectorForeign, std::vector<int>, int);
QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolStdVectorForeign, std::vector<bool>, bool);
QT_DECLARE_SEQUENTIAL_CONTAINER(QStringStdVectorForeign, std::vector<QString>, QString);
QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlStdVectorForeign, std::vector<QUrl>, QUrl);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QIntListForeign, QList<int>, int);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolListForeign, QList<bool>, bool);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QStringListForeign, QStringList, QString);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlListForeign, QList<QUrl>, QUrl);
-
#if QT_CONFIG(qml_itemmodel)
QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexListForeign, QModelIndexList, QModelIndex);
QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexStdVectorForeign, std::vector<QModelIndex>, QModelIndex);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 63e02ce631..c21f1b4e32 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -461,9 +461,17 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(argumentType.sizeOf() > 0);
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
- argumentType.construct(arg);
- if (frame->argc() > i)
- QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+
+ if (argumentType == QMetaType::fromType<QVariant>()) {
+ if (frame->argc() > i)
+ new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]);
+ else
+ new (arg) QVariant();
+ } else {
+ argumentType.construct(arg);
+ if (frame->argc() > i)
+ QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+ }
transformedArguments[i] = arg;
}
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 306f3d40f9..1ffdddc1e1 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1680,6 +1680,9 @@ Type: UiQualifiedId;
} break;
./
+Type: T_VAR;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
Type: T_VOID;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index 1b58f687e2..3ae5c405f2 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -80,31 +80,7 @@ static inline QChar convertHex(QChar c1, QChar c2)
return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
}
-Lexer::Lexer(Engine *engine)
- : _engine(engine)
- , _codePtr(nullptr)
- , _endPtr(nullptr)
- , _tokenStartPtr(nullptr)
- , _char(u'\n')
- , _errorCode(NoError)
- , _currentLineNumber(0)
- , _currentColumnNumber(0)
- , _tokenValue(0)
- , _parenthesesState(IgnoreParentheses)
- , _parenthesesCount(0)
- , _stackToken(-1)
- , _patternFlags(0)
- , _tokenKind(0)
- , _tokenLength(0)
- , _tokenLine(0)
- , _tokenColumn(0)
- , _validTokenText(false)
- , _prohibitAutomaticSemicolon(false)
- , _restrictedKeyword(false)
- , _terminator(false)
- , _followsClosingBrace(false)
- , _delimited(true)
- , _qmlMode(true)
+Lexer::Lexer(Engine *engine) : _engine(engine), _endPtr(nullptr), _qmlMode(true)
{
if (engine)
engine->setLexer(this);
@@ -127,66 +103,67 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_qmlMode = qmlMode;
_code = code;
- _tokenText.clear();
- _tokenText.reserve(1024);
- _errorMessage.clear();
- _tokenSpell = QStringView();
- _rawString = QStringView();
- _codePtr = code.unicode();
- _endPtr = _codePtr + code.length();
- _tokenStartPtr = _codePtr;
+ _state.tokenText.clear();
+ _state.tokenText.reserve(1024);
+ _state.errorMessage.clear();
+ _state.tokenSpell = QStringView();
+ _state.rawString = QStringView();
- _char = u'\n';
- _errorCode = NoError;
+ _state.codePtr = code.unicode();
+ _endPtr = _state.codePtr + code.length();
+ _state.tokenStartPtr = _state.codePtr;
- _currentLineNumber = lineno;
- _currentColumnNumber = 0;
- _tokenValue = 0;
+ _state.currentChar = u'\n';
+ _state.errorCode = NoError;
+
+ _state.currentLineNumber = lineno;
+ _state.currentColumnNumber = 0;
+ _state.tokenValue = 0;
// parentheses state
- _parenthesesState = IgnoreParentheses;
- _parenthesesCount = 0;
-
- _stackToken = -1;
-
- _patternFlags = 0;
- _tokenLength = 0;
- _tokenLine = lineno;
- _tokenColumn = 0;
-
- _validTokenText = false;
- _prohibitAutomaticSemicolon = false;
- _restrictedKeyword = false;
- _terminator = false;
- _followsClosingBrace = false;
- _delimited = true;
+ _state.parenthesesState = IgnoreParentheses;
+ _state.parenthesesCount = 0;
+
+ _state.stackToken = -1;
+
+ _state.patternFlags = 0;
+ _state.tokenLength = 0;
+ _state.tokenLine = lineno;
+ _state.tokenColumn = 0;
+
+ _state.validTokenText = false;
+ _state.prohibitAutomaticSemicolon = false;
+ _state.restrictedKeyword = false;
+ _state.terminator = false;
+ _state.followsClosingBrace = false;
+ _state.delimited = true;
}
void Lexer::scanChar()
{
- if (_skipLinefeed) {
- Q_ASSERT(*_codePtr == u'\n');
- ++_codePtr;
- _skipLinefeed = false;
+ if (_state.skipLinefeed) {
+ Q_ASSERT(*_state.codePtr == u'\n');
+ ++_state.codePtr;
+ _state.skipLinefeed = false;
}
- _char = *_codePtr++;
- ++_currentColumnNumber;
+ _state.currentChar = *_state.codePtr++;
+ ++_state.currentColumnNumber;
if (isLineTerminator()) {
- if (_char == u'\r') {
- if (_codePtr < _endPtr && *_codePtr == u'\n')
- _skipLinefeed = true;
- _char = u'\n';
+ if (_state.currentChar == u'\r') {
+ if (_state.codePtr < _endPtr && *_state.codePtr == u'\n')
+ _state.skipLinefeed = true;
+ _state.currentChar = u'\n';
}
- ++_currentLineNumber;
- _currentColumnNumber = 0;
+ ++_state.currentLineNumber;
+ _state.currentColumnNumber = 0;
}
}
QChar Lexer::peekChar()
{
- auto peekPtr = _codePtr;
+ auto peekPtr = _state.codePtr;
if (peekPtr < _endPtr)
return *peekPtr;
return QChar();
@@ -259,107 +236,107 @@ int octalDigit(QChar c)
int Lexer::lex()
{
- const int previousTokenKind = _tokenKind;
+ const int previousTokenKind = _state.tokenKind;
again:
- _tokenSpell = QStringView();
- _rawString = QStringView();
- _tokenKind = scanToken();
- _tokenLength = _codePtr - _tokenStartPtr - 1;
-
- _delimited = false;
- _restrictedKeyword = false;
- _followsClosingBrace = (previousTokenKind == T_RBRACE);
-
- // update the flags
- switch (_tokenKind) {
- case T_LBRACE:
- if (_bracesCount > 0)
- ++_bracesCount;
- Q_FALLTHROUGH();
- case T_SEMICOLON:
- _importState = ImportState::NoQmlImport;
- Q_FALLTHROUGH();
- case T_QUESTION:
- case T_COLON:
- case T_TILDE:
- _delimited = true;
- break;
- case T_AUTOMATIC_SEMICOLON:
- case T_AS:
- _importState = ImportState::NoQmlImport;
- Q_FALLTHROUGH();
- default:
- if (isBinop(_tokenKind))
- _delimited = true;
- break;
-
- case T_IMPORT:
- if (qmlMode() || (_handlingDirectives && previousTokenKind == T_DOT))
- _importState = ImportState::SawImport;
- if (isBinop(_tokenKind))
- _delimited = true;
- break;
-
- case T_IF:
- case T_FOR:
- case T_WHILE:
- case T_WITH:
- _parenthesesState = CountParentheses;
- _parenthesesCount = 0;
- break;
-
- case T_ELSE:
- case T_DO:
- _parenthesesState = BalancedParentheses;
- break;
-
- case T_CONTINUE:
- case T_BREAK:
- case T_RETURN:
- case T_YIELD:
- case T_THROW:
- _restrictedKeyword = true;
- break;
- case T_RBRACE:
- if (_bracesCount > 0)
- --_bracesCount;
- if (_bracesCount == 0)
- goto again;
- } // switch
+ _state.tokenSpell = QStringView();
+ _state.rawString = QStringView();
+ _state.tokenKind = scanToken();
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
+
+ _state.delimited = false;
+ _state.restrictedKeyword = false;
+ _state.followsClosingBrace = (previousTokenKind == T_RBRACE);
+
+ // update the flags
+ switch (_state.tokenKind) {
+ case T_LBRACE:
+ if (_state.bracesCount > 0)
+ ++_state.bracesCount;
+ Q_FALLTHROUGH();
+ case T_SEMICOLON:
+ _state.importState = ImportState::NoQmlImport;
+ Q_FALLTHROUGH();
+ case T_QUESTION:
+ case T_COLON:
+ case T_TILDE:
+ _state.delimited = true;
+ break;
+ case T_AUTOMATIC_SEMICOLON:
+ case T_AS:
+ _state.importState = ImportState::NoQmlImport;
+ Q_FALLTHROUGH();
+ default:
+ if (isBinop(_state.tokenKind))
+ _state.delimited = true;
+ break;
+
+ case T_IMPORT:
+ if (qmlMode() || (_state.handlingDirectives && previousTokenKind == T_DOT))
+ _state.importState = ImportState::SawImport;
+ if (isBinop(_state.tokenKind))
+ _state.delimited = true;
+ break;
+
+ case T_IF:
+ case T_FOR:
+ case T_WHILE:
+ case T_WITH:
+ _state.parenthesesState = CountParentheses;
+ _state.parenthesesCount = 0;
+ break;
+
+ case T_ELSE:
+ case T_DO:
+ _state.parenthesesState = BalancedParentheses;
+ break;
+
+ case T_CONTINUE:
+ case T_BREAK:
+ case T_RETURN:
+ case T_YIELD:
+ case T_THROW:
+ _state.restrictedKeyword = true;
+ break;
+ case T_RBRACE:
+ if (_state.bracesCount > 0)
+ --_state.bracesCount;
+ if (_state.bracesCount == 0)
+ goto again;
+ } // switch
// update the parentheses state
- switch (_parenthesesState) {
- case IgnoreParentheses:
- break;
-
- case CountParentheses:
- if (_tokenKind == T_RPAREN) {
- --_parenthesesCount;
- if (_parenthesesCount == 0)
- _parenthesesState = BalancedParentheses;
- } else if (_tokenKind == T_LPAREN) {
- ++_parenthesesCount;
- }
- break;
-
- case BalancedParentheses:
- if (_tokenKind != T_DO && _tokenKind != T_ELSE)
- _parenthesesState = IgnoreParentheses;
- break;
- } // switch
-
- return _tokenKind;
+ switch (_state.parenthesesState) {
+ case IgnoreParentheses:
+ break;
+
+ case CountParentheses:
+ if (_state.tokenKind == T_RPAREN) {
+ --_state.parenthesesCount;
+ if (_state.parenthesesCount == 0)
+ _state.parenthesesState = BalancedParentheses;
+ } else if (_state.tokenKind == T_LPAREN) {
+ ++_state.parenthesesCount;
+ }
+ break;
+
+ case BalancedParentheses:
+ if (_state.tokenKind != T_DO && _state.tokenKind != T_ELSE)
+ _state.parenthesesState = IgnoreParentheses;
+ break;
+ } // switch
+
+ return _state.tokenKind;
}
uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
{
- Q_ASSERT(_char == u'u');
+ Q_ASSERT(_state.currentChar == u'u');
scanChar(); // skip u
- if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) {
+ if (_state.codePtr + 4 <= _endPtr && isHexDigit(_state.currentChar)) {
uint codePoint = 0;
for (int i = 0; i < 4; ++i) {
- int digit = hexDigit(_char);
+ int digit = hexDigit(_state.currentChar);
if (digit < 0)
goto error;
codePoint *= 16;
@@ -369,15 +346,15 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
*ok = true;
return codePoint;
- } else if (_codePtr < _endPtr && _char == u'{') {
+ } else if (_state.codePtr < _endPtr && _state.currentChar == u'{') {
scanChar(); // skip '{'
uint codePoint = 0;
- if (!isHexDigit(_char))
+ if (!isHexDigit(_state.currentChar))
// need at least one hex digit
goto error;
- while (_codePtr <= _endPtr) {
- int digit = hexDigit(_char);
+ while (_state.codePtr <= _endPtr) {
+ int digit = hexDigit(_state.currentChar);
if (digit < 0)
break;
codePoint *= 16;
@@ -387,7 +364,7 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
scanChar();
}
- if (_char != u'}')
+ if (_state.currentChar != u'}')
goto error;
scanChar(); // skip '}'
@@ -398,8 +375,9 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
}
error:
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ _state.errorCode = IllegalUnicodeEscapeSequence;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
*ok = false;
return 0;
@@ -407,13 +385,13 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
QChar Lexer::decodeHexEscapeCharacter(bool *ok)
{
- if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) {
+ if (isHexDigit(_state.codePtr[0]) && isHexDigit(_state.codePtr[1])) {
scanChar();
- const QChar c1 = _char;
+ const QChar c1 = _state.currentChar;
scanChar();
- const QChar c2 = _char;
+ const QChar c2 = _state.currentChar;
scanChar();
if (ok)
@@ -481,36 +459,36 @@ static bool isIdentifierPart(uint ch)
int Lexer::scanToken()
{
- if (_stackToken != -1) {
- int tk = _stackToken;
- _stackToken = -1;
+ if (_state.stackToken != -1) {
+ int tk = _state.stackToken;
+ _state.stackToken = -1;
return tk;
}
- if (_bracesCount == 0) {
+ if (_state.bracesCount == 0) {
// we're inside a Template string
return scanString(TemplateContinuation);
}
-
- _terminator = false;
+ _state.terminator = false;
again:
- _validTokenText = false;
+ _state.validTokenText = false;
// handle comment can be called after a '/' has been read
// and returns true if it actually encountered a comment
- auto handleComment = [this](){
- if (_char == u'*') {
+ auto handleComment = [this]() {
+ if (_state.currentChar == u'*') {
scanChar();
- while (_codePtr <= _endPtr) {
- if (_char == u'*') {
+ while (_state.codePtr <= _endPtr) {
+ if (_state.currentChar == u'*') {
scanChar();
- if (_char == u'/') {
+ if (_state.currentChar == u'/') {
scanChar();
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
+ _engine->addComment(tokenOffset() + 2,
+ _state.codePtr - _state.tokenStartPtr - 1 - 4,
tokenStartLine(), tokenStartColumn() + 2);
}
@@ -520,30 +498,30 @@ again:
scanChar();
}
}
- } else if (_char == u'/') {
- while (_codePtr <= _endPtr && !isLineTerminator()) {
+ } else if (_state.currentChar == u'/') {
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
scanChar();
}
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
- tokenStartLine(), tokenStartColumn() + 2);
+ _engine->addComment(tokenOffset() + 2,
+ _state.codePtr - _state.tokenStartPtr - 1 - 2, tokenStartLine(),
+ tokenStartColumn() + 2);
}
return true;
}
return false;
};
-
- while (_char.isSpace()) {
+ while (_state.currentChar.isSpace()) {
if (isLineTerminator()) {
- if (_restrictedKeyword) {
+ if (_state.restrictedKeyword) {
// automatic semicolon insertion
- _tokenLine = _currentLineNumber;
- _tokenColumn = _currentColumnNumber;
- _tokenStartPtr = _codePtr - 1;
+ _state.tokenLine = _state.currentLineNumber;
+ _state.tokenColumn = _state.currentColumnNumber;
+ _state.tokenStartPtr = _state.codePtr - 1;
return T_SEMICOLON;
} else {
- _terminator = true;
+ _state.terminator = true;
syncProhibitAutomaticSemicolon();
}
}
@@ -551,14 +529,14 @@ again:
scanChar();
}
- _tokenStartPtr = _codePtr - 1;
- _tokenLine = _currentLineNumber;
- _tokenColumn = _currentColumnNumber;
+ _state.tokenStartPtr = _state.codePtr - 1;
+ _state.tokenLine = _state.currentLineNumber;
+ _state.tokenColumn = _state.currentColumnNumber;
- if (_codePtr > _endPtr)
+ if (_state.codePtr > _endPtr)
return EOF_SYMBOL;
- const QChar ch = _char;
+ const QChar ch = _state.currentChar;
scanChar();
switch (ch.unicode()) {
@@ -566,10 +544,10 @@ again:
case u'}': return T_RBRACE;
case u'|':
- if (_char == u'|') {
+ if (_state.currentChar == u'|') {
scanChar();
return T_OR_OR;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_OR_EQ;
}
@@ -578,7 +556,7 @@ again:
case u'{': return T_LBRACE;
case u'^':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_XOR_EQ;
}
@@ -587,11 +565,11 @@ again:
case u']': return T_RBRACKET;
case u'[': return T_LBRACKET;
case u'?': {
- if (_char == u'?') {
+ if (_state.currentChar == u'?') {
scanChar();
return T_QUESTION_QUESTION;
}
- if (_char == u'.' && !peekChar().isDigit()) {
+ if (_state.currentChar == u'.' && !peekChar().isDigit()) {
scanChar();
return T_QUESTION_DOT;
}
@@ -600,47 +578,47 @@ again:
}
case u'>':
- if (_char == u'>') {
+ if (_state.currentChar == u'>') {
scanChar();
- if (_char == u'>') {
+ if (_state.currentChar == u'>') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_GT_GT_GT_EQ;
}
return T_GT_GT_GT;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_GT_GT_EQ;
}
return T_GT_GT;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_GE;
}
return T_GT;
case u'=':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_EQ_EQ_EQ;
}
return T_EQ_EQ;
- } else if (_char == u'>') {
+ } else if (_state.currentChar == u'>') {
scanChar();
return T_ARROW;
}
return T_EQ;
case u'<':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_LE;
- } else if (_char == u'<') {
+ } else if (_state.currentChar == u'<') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_LT_LT_EQ;
}
@@ -654,39 +632,41 @@ again:
case u'/':
if (handleComment())
goto again;
- else if (_char == u'=') {
+ else if (_state.currentChar == u'=') {
scanChar();
return T_DIVIDE_EQ;
}
return T_DIVIDE_;
case u'.':
- if (_importState == ImportState::SawImport)
+ if (_state.importState == ImportState::SawImport)
return T_DOT;
- if (isDecimalDigit(_char.unicode()))
+ if (isDecimalDigit(_state.currentChar.unicode()))
return scanNumber(ch);
- if (_char == u'.') {
+ if (_state.currentChar == u'.') {
scanChar();
- if (_char == u'.') {
+ if (_state.currentChar == u'.') {
scanChar();
return T_ELLIPSIS;
} else {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
return T_ERROR;
}
}
return T_DOT;
case u'-':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_MINUS_EQ;
- } else if (_char == u'-') {
+ } else if (_state.currentChar == u'-') {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
- _stackToken = T_MINUS_MINUS;
+ if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon
+ && _state.tokenKind != T_LPAREN) {
+ _state.stackToken = T_MINUS_MINUS;
return T_SEMICOLON;
}
@@ -697,14 +677,15 @@ again:
case u',': return T_COMMA;
case u'+':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_PLUS_EQ;
- } else if (_char == u'+') {
+ } else if (_state.currentChar == u'+') {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
- _stackToken = T_PLUS_PLUS;
+ if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon
+ && _state.tokenKind != T_LPAREN) {
+ _state.stackToken = T_PLUS_PLUS;
return T_SEMICOLON;
}
@@ -713,12 +694,12 @@ again:
return T_PLUS;
case u'*':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_STAR_EQ;
- } else if (_char == u'*') {
+ } else if (_state.currentChar == u'*') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_STAR_STAR_EQ;
}
@@ -732,26 +713,26 @@ again:
case u'@': return T_AT;
case u'&':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_AND_EQ;
- } else if (_char == u'&') {
+ } else if (_state.currentChar == u'&') {
scanChar();
return T_AND_AND;
}
return T_AND;
case u'%':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_REMAINDER_EQ;
}
return T_REMAINDER;
case u'!':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_NOT_EQ_EQ;
}
@@ -760,7 +741,7 @@ again:
return T_NOT;
case u'`':
- _outerTemplateBraceCount.push(_bracesCount);
+ _state.outerTemplateBraceCount.push(_state.bracesCount);
Q_FALLTHROUGH();
case u'\'':
case u'"':
@@ -775,19 +756,19 @@ again:
case u'7':
case u'8':
case u'9':
- if (_importState == ImportState::SawImport)
+ if (_state.importState == ImportState::SawImport)
return scanVersionNumber(ch);
else
return scanNumber(ch);
case '#':
- if (_currentLineNumber == 1 && _currentColumnNumber == 2) {
+ if (_state.currentLineNumber == 1 && _state.currentColumnNumber == 2) {
// shebang support
- while (_codePtr <= _endPtr && !isLineTerminator()) {
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
scanChar();
}
if (_engine) {
- _engine->addComment(tokenOffset(), _codePtr - _tokenStartPtr - 1,
+ _engine->addComment(tokenOffset(), _state.codePtr - _state.tokenStartPtr - 1,
tokenStartLine(), tokenStartColumn());
}
goto again;
@@ -797,10 +778,10 @@ again:
default: {
uint c = ch.unicode();
bool identifierWithEscapeChars = false;
- if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) {
- c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_state.currentChar.unicode())) {
+ c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode());
scanChar();
- } else if (c == '\\' && _char == u'u') {
+ } else if (c == '\\' && _state.currentChar == u'u') {
identifierWithEscapeChars = true;
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
@@ -809,26 +790,27 @@ again:
}
if (isIdentifierStart(c)) {
if (identifierWithEscapeChars) {
- _tokenText.resize(0);
+ _state.tokenText.resize(0);
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
- _validTokenText = true;
+ _state.validTokenText = true;
}
- while (_codePtr <= _endPtr) {
- c = _char.unicode();
- if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) {
+ while (_state.codePtr <= _endPtr) {
+ c = _state.currentChar.unicode();
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_state.codePtr->unicode())) {
scanChar();
- c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
- } else if (_char == u'\\' && _codePtr[0] == u'u') {
+ c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode());
+ } else if (_state.currentChar == u'\\' && _state.codePtr[0] == u'u') {
if (!identifierWithEscapeChars) {
identifierWithEscapeChars = true;
- _tokenText.resize(0);
- _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
- _validTokenText = true;
+ _state.tokenText.resize(0);
+ _state.tokenText.insert(0, _state.tokenStartPtr,
+ _state.codePtr - _state.tokenStartPtr - 1);
+ _state.validTokenText = true;
}
scanChar(); // skip '\\'
@@ -841,10 +823,10 @@ again:
break;
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
continue;
}
@@ -854,42 +836,43 @@ again:
if (identifierWithEscapeChars) {
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
}
scanChar();
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
int kind = T_IDENTIFIER;
if (!identifierWithEscapeChars)
- kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags());
+ kind = classify(_state.tokenStartPtr, _state.tokenLength, parseModeFlags());
if (kind == T_FUNCTION) {
continue_skipping:
- while (_codePtr < _endPtr && _char.isSpace())
- scanChar();
- if (_char == u'*') {
- _tokenLength = _codePtr - _tokenStartPtr - 1;
- kind = T_FUNCTION_STAR;
- scanChar();
- } else if (_char == u'/') {
- scanChar();
- if (handleComment())
- goto continue_skipping;
- }
+ while (_state.codePtr < _endPtr && _state.currentChar.isSpace())
+ scanChar();
+ if (_state.currentChar == u'*') {
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
+ kind = T_FUNCTION_STAR;
+ scanChar();
+ } else if (_state.currentChar == u'/') {
+ scanChar();
+ if (handleComment())
+ goto continue_skipping;
+ }
}
if (_engine) {
if (kind == T_IDENTIFIER && identifierWithEscapeChars)
- _tokenSpell = _engine->newStringRef(_tokenText);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
else
- _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
+ _state.tokenSpell = _engine->midRef(_state.tokenStartPtr - _code.unicode(),
+ _state.tokenLength);
}
return kind;
@@ -907,34 +890,36 @@ int Lexer::scanString(ScanStringMode mode)
QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
bool multilineStringLiteral = false;
- const QChar *startCode = _codePtr - 1;
+ const QChar *startCode = _state.codePtr - 1;
// in case we just parsed a \r, we need to reset this flag to get things working
// correctly in the loop below and afterwards
- _skipLinefeed = false;
+ _state.skipLinefeed = false;
bool first = true;
if (_engine) {
- while (_codePtr <= _endPtr) {
+ while (_state.codePtr <= _endPtr) {
if (isLineTerminator()) {
if ((quote == u'`' || qmlMode())) {
if (first)
- --_currentLineNumber; // will be read again in scanChar()
+ --_state.currentLineNumber; // will be read again in scanChar()
break;
}
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Stray newline in string literal");
return T_ERROR;
- } else if (_char == u'\\') {
+ } else if (_state.currentChar == u'\\') {
break;
- } else if (_char == u'$' && quote == u'`') {
+ } else if (_state.currentChar == u'$' && quote == u'`') {
break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1);
- _rawString = _tokenSpell;
+ } else if (_state.currentChar == quote) {
+ _state.tokenSpell = _engine->midRef(startCode - _code.unicode(),
+ _state.codePtr - startCode - 1);
+ _state.rawString = _state.tokenSpell;
scanChar();
if (quote == u'`')
- _bracesCount = _outerTemplateBraceCount.pop();
+ _state.bracesCount = _state.outerTemplateBraceCount.pop();
if (mode == TemplateHead)
return T_NO_SUBSTITUTION_TEMPLATE;
@@ -944,40 +929,40 @@ int Lexer::scanString(ScanStringMode mode)
return T_STRING_LITERAL;
}
// don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result
- _char = *_codePtr++;
- ++_currentColumnNumber;
+ _state.currentChar = *_state.codePtr++;
+ ++_state.currentColumnNumber;
first = false;
}
}
// rewind by one char, so things gets scanned correctly
- --_codePtr;
- --_currentColumnNumber;
+ --_state.codePtr;
+ --_state.currentColumnNumber;
- _validTokenText = true;
- _tokenText = QString(startCode, _codePtr - startCode);
+ _state.validTokenText = true;
+ _state.tokenText = QString(startCode, _state.codePtr - startCode);
auto setRawString = [&](const QChar *end) {
QString raw(startCode, end - startCode - 1);
raw.replace(QLatin1String("\r\n"), QLatin1String("\n"));
raw.replace(u'\r', u'\n');
- _rawString = _engine->newStringRef(raw);
+ _state.rawString = _engine->newStringRef(raw);
};
scanChar();
- while (_codePtr <= _endPtr) {
- if (_char == quote) {
+ while (_state.codePtr <= _endPtr) {
+ if (_state.currentChar == quote) {
scanChar();
if (_engine) {
- _tokenSpell = _engine->newStringRef(_tokenText);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
if (quote == u'`')
- setRawString(_codePtr - 1);
+ setRawString(_state.codePtr - 1);
}
if (quote == u'`')
- _bracesCount = _outerTemplateBraceCount.pop();
+ _state.bracesCount = _state.outerTemplateBraceCount.pop();
if (mode == TemplateContinuation)
return T_TEMPLATE_TAIL;
@@ -985,27 +970,28 @@ int Lexer::scanString(ScanStringMode mode)
return T_NO_SUBSTITUTION_TEMPLATE;
return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
- } else if (quote == u'`' && _char == u'$' && *_codePtr == u'{') {
+ } else if (quote == u'`' && _state.currentChar == u'$' && *_state.codePtr == u'{') {
scanChar();
scanChar();
- _bracesCount = 1;
+ _state.bracesCount = 1;
if (_engine) {
- _tokenSpell = _engine->newStringRef(_tokenText);
- setRawString(_codePtr - 2);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
+ setRawString(_state.codePtr - 2);
}
return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
- } else if (_char == u'\\') {
+ } else if (_state.currentChar == u'\\') {
scanChar();
- if (_codePtr > _endPtr) {
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ if (_state.codePtr > _endPtr) {
+ _state.errorCode = IllegalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "End of file reached at escape sequence");
return T_ERROR;
}
QChar u;
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
// unicode escape sequence
case u'u': {
bool ok = false;
@@ -1014,7 +1000,7 @@ int Lexer::scanString(ScanStringMode mode)
return T_ERROR;
if (QChar::requiresSurrogates(codePoint)) {
// need to use a surrogate pair
- _tokenText += QChar(QChar::highSurrogate(codePoint));
+ _state.tokenText += QChar(QChar::highSurrogate(codePoint));
u = QChar::lowSurrogate(codePoint);
} else {
u = QChar(codePoint);
@@ -1026,8 +1012,9 @@ int Lexer::scanString(ScanStringMode mode)
bool ok = false;
u = decodeHexEscapeCharacter(&ok);
if (!ok) {
- _errorCode = IllegalHexadecimalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
+ _state.errorCode = IllegalHexadecimalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Illegal hexadecimal escape sequence");
return T_ERROR;
}
} break;
@@ -1044,7 +1031,7 @@ int Lexer::scanString(ScanStringMode mode)
case u'v': u = u'\v'; scanChar(); break;
case u'0':
- if (! _codePtr->isDigit()) {
+ if (!_state.codePtr->isDigit()) {
scanChar();
u = u'\0';
break;
@@ -1059,8 +1046,9 @@ int Lexer::scanString(ScanStringMode mode)
case u'7':
case u'8':
case u'9':
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
+ _state.errorCode = IllegalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Octal escape sequences are not allowed");
return T_ERROR;
case u'\r':
@@ -1072,40 +1060,45 @@ int Lexer::scanString(ScanStringMode mode)
default:
// non escape character
- u = _char;
+ u = _state.currentChar;
scanChar();
}
- _tokenText += u;
+ _state.tokenText += u;
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
}
- _errorCode = UnclosedStringLiteral;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
+ _state.errorCode = UnclosedStringLiteral;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
return T_ERROR;
}
int Lexer::scanNumber(QChar ch)
{
if (ch == u'0') {
- if (_char == u'x' || _char == u'X') {
- ch = _char; // remember the x or X to use it in the error message below.
+ if (_state.currentChar == u'x' || _state.currentChar == u'X') {
+ ch = _state.currentChar; // remember the x or X to use it in the error message below.
// parse hex integer literal
scanChar(); // consume 'x'
- if (!isHexDigit(_char)) {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ if (!isHexDigit(_state.currentChar)) {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser",
+ "At least one hexadecimal digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
- int digit = ::hexDigit(_char);
+ int digit = ::hexDigit(_state.currentChar);
if (digit < 0)
break;
d *= 16;
@@ -1113,23 +1106,26 @@ int Lexer::scanNumber(QChar ch)
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char == u'o' || _char == u'O') {
- ch = _char; // remember the o or O to use it in the error message below.
+ } else if (_state.currentChar == u'o' || _state.currentChar == u'O') {
+ ch = _state.currentChar; // remember the o or O to use it in the error message below.
// parse octal integer literal
scanChar(); // consume 'o'
- if (!isOctalDigit(_char.unicode())) {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch);
+ if (!isOctalDigit(_state.currentChar.unicode())) {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser", "At least one octal digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
- int digit = ::octalDigit(_char);
+ int digit = ::octalDigit(_state.currentChar);
if (digit < 0)
break;
d *= 8;
@@ -1137,37 +1133,41 @@ int Lexer::scanNumber(QChar ch)
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char == u'b' || _char == u'B') {
- ch = _char; // remember the b or B to use it in the error message below.
+ } else if (_state.currentChar == u'b' || _state.currentChar == u'B') {
+ ch = _state.currentChar; // remember the b or B to use it in the error message below.
// parse binary integer literal
scanChar(); // consume 'b'
- if (_char.unicode() != u'0' && _char.unicode() != u'1') {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch);
+ if (_state.currentChar.unicode() != u'0' && _state.currentChar.unicode() != u'1') {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser", "At least one binary digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
int digit = 0;
- if (_char.unicode() == u'1')
+ if (_state.currentChar.unicode() == u'1')
digit = 1;
- else if (_char.unicode() != u'0')
+ else if (_state.currentChar.unicode() != u'0')
break;
d *= 2;
d += digit;
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char.isDigit() && !qmlMode()) {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
+ } else if (_state.currentChar.isDigit() && !qmlMode()) {
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Decimal numbers can't start with '0'");
return T_ERROR;
}
}
@@ -1177,36 +1177,37 @@ int Lexer::scanNumber(QChar ch)
chars.append(ch.unicode());
if (ch != u'.') {
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume the digit
}
- if (_char == u'.') {
- chars.append(_char.unicode());
+ if (_state.currentChar == u'.') {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume `.'
}
}
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar();
}
- if (_char == u'e' || _char == u'E') {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == u'+' || _codePtr[0] == u'-') &&
- _codePtr[1].isDigit())) {
+ if (_state.currentChar == u'e' || _state.currentChar == u'E') {
+ if (_state.codePtr[0].isDigit()
+ || ((_state.codePtr[0] == u'+' || _state.codePtr[0] == u'-')
+ && _state.codePtr[1].isDigit())) {
- chars.append(_char.unicode());
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume `e'
- if (_char == u'+' || _char == u'-') {
- chars.append(_char.unicode());
+ if (_state.currentChar == u'+' || _state.currentChar == u'-') {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume the sign
}
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar();
}
}
@@ -1216,11 +1217,12 @@ int Lexer::scanNumber(QChar ch)
const char *end = nullptr;
bool ok = false;
- _tokenValue = qstrntod(begin, chars.size(), &end, &ok);
+ _state.tokenValue = qstrntod(begin, chars.size(), &end, &ok);
if (end - begin != chars.size()) {
- _errorCode = IllegalExponentIndicator;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
+ _state.errorCode = IllegalExponentIndicator;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
return T_ERROR;
}
@@ -1230,108 +1232,114 @@ int Lexer::scanNumber(QChar ch)
int Lexer::scanVersionNumber(QChar ch)
{
if (ch == u'0') {
- _tokenValue = 0;
+ _state.tokenValue = 0;
return T_VERSION_NUMBER;
}
int acc = 0;
acc += ch.digitValue();
- while (_char.isDigit()) {
+ while (_state.currentChar.isDigit()) {
acc *= 10;
- acc += _char.digitValue();
+ acc += _state.currentChar.digitValue();
scanChar(); // consume the digit
}
- _tokenValue = acc;
+ _state.tokenValue = acc;
return T_VERSION_NUMBER;
}
bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
{
- _tokenText.resize(0);
- _validTokenText = true;
- _patternFlags = 0;
+ _state.tokenText.resize(0);
+ _state.validTokenText = true;
+ _state.patternFlags = 0;
if (prefix == EqualPrefix)
- _tokenText += u'=';
+ _state.tokenText += u'=';
while (true) {
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
case u'/':
scanChar();
// scan the flags
- _patternFlags = 0;
- while (isIdentLetter(_char)) {
- int flag = regExpFlagFromChar(_char);
- if (flag == 0 || _patternFlags & flag) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Invalid regular expression flag '%0'")
- .arg(QChar(_char));
+ _state.patternFlags = 0;
+ while (isIdentLetter(_state.currentChar)) {
+ int flag = regExpFlagFromChar(_state.currentChar);
+ if (flag == 0 || _state.patternFlags & flag) {
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser",
+ "Invalid regular expression flag '%0'")
+ .arg(QChar(_state.currentChar));
return false;
}
- _patternFlags |= flag;
+ _state.patternFlags |= flag;
scanChar();
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
return true;
case u'\\':
// regular expression backslash sequence
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression backslash sequence");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
break;
case u'[':
// regular expression class
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- while (_codePtr <= _endPtr && ! isLineTerminator()) {
- if (_char == u']')
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
+ if (_state.currentChar == u']')
break;
- else if (_char == u'\\') {
+ else if (_state.currentChar == u'\\') {
// regular expression backslash sequence
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression backslash sequence");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
}
- if (_char != u']') {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression class");
+ if (_state.currentChar != u']') {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression class");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar(); // skip ]
break;
default:
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression literal");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression literal");
return false;
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
} // switch
@@ -1342,7 +1350,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
bool Lexer::isLineTerminator() const
{
- const ushort unicode = _char.unicode();
+ const ushort unicode = _state.currentChar.unicode();
return unicode == 0x000Au
|| unicode == 0x000Du
|| unicode == 0x2028u
@@ -1351,13 +1359,13 @@ bool Lexer::isLineTerminator() const
unsigned Lexer::isLineTerminatorSequence() const
{
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
case 0x000Au:
case 0x2028u:
case 0x2029u:
return 1;
case 0x000Du:
- if (_codePtr->unicode() == 0x000Au)
+ if (_state.codePtr->unicode() == 0x000Au)
return 2;
else
return 1;
@@ -1398,54 +1406,52 @@ bool Lexer::isOctalDigit(ushort c)
QString Lexer::tokenText() const
{
- if (_validTokenText)
- return _tokenText;
+ if (_state.validTokenText)
+ return _state.tokenText;
- if (_tokenKind == T_STRING_LITERAL)
- return QString(_tokenStartPtr + 1, _tokenLength - 2);
+ if (_state.tokenKind == T_STRING_LITERAL)
+ return QString(_state.tokenStartPtr + 1, _state.tokenLength - 2);
- return QString(_tokenStartPtr, _tokenLength);
+ return QString(_state.tokenStartPtr, _state.tokenLength);
}
Lexer::Error Lexer::errorCode() const
{
- return _errorCode;
+ return _state.errorCode;
}
QString Lexer::errorMessage() const
{
- return _errorMessage;
+ return _state.errorMessage;
}
void Lexer::syncProhibitAutomaticSemicolon()
{
- if (_parenthesesState == BalancedParentheses) {
+ if (_state.parenthesesState == BalancedParentheses) {
// we have seen something like "if (foo)", which means we should
// never insert an automatic semicolon at this point, since it would
// then be expanded into an empty statement (ECMA-262 7.9.1)
- _prohibitAutomaticSemicolon = true;
- _parenthesesState = IgnoreParentheses;
+ _state.prohibitAutomaticSemicolon = true;
+ _state.parenthesesState = IgnoreParentheses;
} else {
- _prohibitAutomaticSemicolon = false;
+ _state.prohibitAutomaticSemicolon = false;
}
}
bool Lexer::prevTerminator() const
{
- return _terminator;
+ return _state.terminator;
}
bool Lexer::followsClosingBrace() const
{
- return _followsClosingBrace;
+ return _state.followsClosingBrace;
}
bool Lexer::canInsertAutomaticSemicolon(int token) const
{
- return token == T_RBRACE
- || token == EOF_SYMBOL
- || _terminator
- || _followsClosingBrace;
+ return token == T_RBRACE || token == EOF_SYMBOL || _state.terminator
+ || _state.followsClosingBrace;
}
static const int uriTokens[] = {
@@ -1509,12 +1515,12 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
error->loc.startColumn = tokenStartColumn();
};
- QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true);
+ QScopedValueRollback<bool> directivesGuard(_state.handlingDirectives, true);
Q_ASSERT(!_qmlMode);
lex(); // fetch the first token
- if (_tokenKind != T_DOT)
+ if (_state.tokenKind != T_DOT)
return true;
do {
@@ -1523,7 +1529,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
lex(); // skip T_DOT
- if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_IMPORT))
+ if (!(_state.tokenKind == T_IDENTIFIER || _state.tokenKind == T_IMPORT))
return true; // expected a valid QML/JS directive
const QString directiveName = tokenText();
@@ -1553,7 +1559,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
QString version;
bool fileImport = false; // file or uri import
- if (_tokenKind == T_STRING_LITERAL) {
+ if (_state.tokenKind == T_STRING_LITERAL) {
// .import T_STRING_LITERAL as T_IDENTIFIER
fileImport = true;
@@ -1565,10 +1571,10 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
}
lex();
- } else if (_tokenKind == T_IDENTIFIER) {
+ } else if (_state.tokenKind == T_IDENTIFIER) {
// .import T_IDENTIFIER (. T_IDENTIFIER)* (T_VERSION_NUMBER (. T_VERSION_NUMBER)?)? as T_IDENTIFIER
while (true) {
- if (!isUriToken(_tokenKind)) {
+ if (!isUriToken(_state.tokenKind)) {
setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
@@ -1580,7 +1586,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
- if (_tokenKind != QQmlJSGrammar::T_DOT)
+ if (_state.tokenKind != QQmlJSGrammar::T_DOT)
break;
pathOrUri.append(u'.');
@@ -1592,13 +1598,13 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
}
}
- if (_tokenKind == T_VERSION_NUMBER) {
+ if (_state.tokenKind == T_VERSION_NUMBER) {
version = tokenText();
lex();
- if (_tokenKind == T_DOT) {
+ if (_state.tokenKind == T_DOT) {
version += u'.';
lex();
- if (_tokenKind != T_VERSION_NUMBER) {
+ if (_state.tokenKind != T_VERSION_NUMBER) {
setError(QCoreApplication::translate(
"QQmlParser", "Incomplete version number (dot but no minor)"));
return false; // expected the module version number
@@ -1612,7 +1618,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
//
// recognize the mandatory `as' followed by the module name
//
- if (! (_tokenKind == T_AS && tokenStartLine() == lineNumber)) {
+ if (!(_state.tokenKind == T_AS && tokenStartLine() == lineNumber)) {
if (fileImport)
setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier"));
else
@@ -1651,7 +1657,16 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
// fetch the first token after the .pragma/.import directive
lex();
- } while (_tokenKind == T_DOT);
+ } while (_state.tokenKind == T_DOT);
return true;
}
+
+const Lexer::State &Lexer::state() const
+{
+ return _state;
+}
+void Lexer::setState(const Lexer::State &state)
+{
+ _state = state;
+}
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index b7ac4a3940..8c2f526de5 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -144,7 +144,7 @@ public:
}
bool qmlMode() const;
- bool yieldIsKeyWord() const { return _generatorLevel != 0; }
+ bool yieldIsKeyWord() const { return _state.generatorLevel != 0; }
void setStaticIsKeyword(bool b) { _staticIsKeyword = b; }
QString code() const;
@@ -155,19 +155,19 @@ public:
bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
bool scanDirectives(Directives *directives, DiagnosticMessage *error);
- int regExpFlags() const { return _patternFlags; }
- QString regExpPattern() const { return _tokenText; }
+ int regExpFlags() const { return _state.patternFlags; }
+ QString regExpPattern() const { return _state.tokenText; }
- int tokenKind() const { return _tokenKind; }
- int tokenOffset() const { return _tokenStartPtr - _code.unicode(); }
- int tokenLength() const { return _tokenLength; }
+ int tokenKind() const { return _state.tokenKind; }
+ int tokenOffset() const { return _state.tokenStartPtr - _code.unicode(); }
+ int tokenLength() const { return _state.tokenLength; }
- int tokenStartLine() const { return _tokenLine; }
- int tokenStartColumn() const { return _tokenColumn; }
+ int tokenStartLine() const { return _state.tokenLine; }
+ int tokenStartColumn() const { return _state.tokenColumn; }
- inline QStringView tokenSpell() const { return _tokenSpell; }
- inline QStringView rawString() const { return _rawString; }
- double tokenValue() const { return _tokenValue; }
+ inline QStringView tokenSpell() const { return _state.tokenSpell; }
+ inline QStringView rawString() const { return _state.rawString; }
+ double tokenValue() const { return _state.tokenValue; }
QString tokenText() const;
Error errorCode() const;
@@ -183,8 +183,56 @@ public:
BalancedParentheses
};
- void enterGeneratorBody() { ++_generatorLevel; }
- void leaveGeneratorBody() { --_generatorLevel; }
+ void enterGeneratorBody() { ++_state.generatorLevel; }
+ void leaveGeneratorBody() { --_state.generatorLevel; }
+
+ struct State
+ {
+ QString tokenText;
+ QString errorMessage;
+ QStringView tokenSpell;
+ QStringView rawString;
+
+ const QChar *codePtr = nullptr;
+ const QChar *tokenStartPtr = nullptr;
+
+ QChar currentChar = u'\n';
+ Error errorCode = NoError;
+
+ int currentLineNumber = 0;
+ int currentColumnNumber = 0;
+ double tokenValue = 0;
+
+ // parentheses state
+ ParenthesesState parenthesesState = IgnoreParentheses;
+ int parenthesesCount = 0;
+
+ // template string stack
+ QStack<int> outerTemplateBraceCount;
+ int bracesCount = -1;
+
+ int stackToken = -1;
+
+ int patternFlags = 0;
+ int tokenKind = 0;
+ int tokenLength = 0;
+ int tokenLine = 0;
+ int tokenColumn = 0;
+ ImportState importState = ImportState::NoQmlImport;
+
+ bool validTokenText = false;
+ bool prohibitAutomaticSemicolon = false;
+ bool restrictedKeyword = false;
+ bool terminator = false;
+ bool followsClosingBrace = false;
+ bool delimited = true;
+ bool skipLinefeed = false;
+ bool handlingDirectives = false;
+ int generatorLevel = 0;
+ };
+
+ const State &state() const;
+ void setState(const State &state);
protected:
static int classify(const QChar *s, int n, int parseModeFlags);
@@ -218,50 +266,11 @@ private:
Engine *_engine;
QString _code;
- QString _tokenText;
- QString _errorMessage;
- QStringView _tokenSpell;
- QStringView _rawString;
-
- const QChar *_codePtr;
const QChar *_endPtr;
- const QChar *_tokenStartPtr;
-
- QChar _char;
- Error _errorCode;
-
- int _currentLineNumber;
- int _currentColumnNumber;
- double _tokenValue;
-
- // parentheses state
- ParenthesesState _parenthesesState;
- int _parenthesesCount;
-
- // template string stack
- QStack<int> _outerTemplateBraceCount;
- int _bracesCount = -1;
-
- int _stackToken;
-
- int _patternFlags;
- int _tokenKind;
- int _tokenLength;
- int _tokenLine;
- int _tokenColumn;
- ImportState _importState = ImportState::NoQmlImport;
-
- bool _validTokenText;
- bool _prohibitAutomaticSemicolon;
- bool _restrictedKeyword;
- bool _terminator;
- bool _followsClosingBrace;
- bool _delimited;
bool _qmlMode;
- bool _skipLinefeed = false;
- int _generatorLevel = 0;
bool _staticIsKeyword = false;
- bool _handlingDirectives = false;
+
+ State _state;
};
} // end of namespace QQmlJS
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index cfcc8d5a2b..43cb973691 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -379,9 +379,9 @@ static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
return revisions;
const int propertyOffset = metaObject->propertyOffset();
const int propertyCount = metaObject->propertyCount();
- for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
- propertyIndex < propertyEnd; ++propertyIndex) {
- const QMetaProperty property = metaObject->property(propertyIndex);
+ for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
+ coreIndex < propertyEnd; ++coreIndex) {
+ const QMetaProperty property = metaObject->property(coreIndex);
if (int revision = property.revision())
revisions.append(QTypeRevision::fromEncodedVersion(revision));
}
@@ -528,21 +528,23 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
uniqueRevisions(&revisions, type.version, added);
for (QTypeRevision revision : revisions) {
- if (revision < added)
- continue;
if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
break;
- // When removed, we still add revisions, but anonymous ones
- if (removed.isValid() && !(revision < removed)) {
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
typeRevision.elementName = nullptr;
typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
} else {
typeRevision.elementName = elementName;
typeRevision.create = creatable ? type.create : nullptr;
typeRevision.userdata = type.userdata;
}
- assignVersions(&typeRevision, revision, type.version);
typeRevision.customParser = type.customParserFactory();
const int id = qmlregister(TypeRegistration, &typeRevision);
if (type.qmlTypeIds)
@@ -589,13 +591,14 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
uniqueRevisions(&revisions, type.version, added);
for (QTypeRevision revision : qAsConst(revisions)) {
- if (revision < added)
- continue;
if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
break;
- // When removed, we still add revisions, but anonymous ones
- if (removed.isValid() && !(revision < removed)) {
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
revisionRegistration.typeName = nullptr;
revisionRegistration.qObjectApi = nullptr;
} else {
@@ -603,7 +606,6 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
revisionRegistration.qObjectApi = type.qObjectApi;
}
- assignVersions(&revisionRegistration, revision, type.version);
const int id = qmlregister(SingletonRegistration, &revisionRegistration);
if (type.qmlTypeIds)
type.qmlTypeIds->append(id);
@@ -773,6 +775,21 @@ void AOTCompiledContext::setReturnValueUndefined() const
}
}
+static void captureFallbackProperty(
+ QObject *object, int coreIndex, int notifyIndex, bool isConstant,
+ QQmlContextData *qmlContext)
+{
+ if (!qmlContext || isConstant)
+ return;
+
+ QQmlEngine *engine = qmlContext->engine();
+ Q_ASSERT(engine);
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ Q_ASSERT(ep);
+ if (QQmlPropertyCapture *capture = ep->propertyCapture)
+ capture->captureProperty(object, coreIndex, notifyIndex);
+}
+
static void captureObjectProperty(
QObject *object, const QQmlPropertyCache *propertyCache,
const QQmlPropertyData *property, QQmlContextData *qmlContext)
@@ -799,8 +816,8 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
-static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object, void *target,
- QQmlContextData *qmlContext)
+static ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{
QQmlData *qmlData = QQmlData::get(object);
if (!qmlData)
@@ -822,6 +839,33 @@ static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object,
return ObjectPropertyResult::OK;
}
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+{
+ QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
+ qmlData->flushPendingBinding(coreIndex);
+
+ captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+
+ void *a[] = { target, nullptr };
+ metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{
const QQmlData *qmlData = QQmlData::get(object);
@@ -838,7 +882,62 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object,
return ObjectPropertyResult::OK;
}
-static bool initObjectLookup(
+static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ const QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
+
+ void *args[] = { value, nullptr };
+ metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
+ return ObjectPropertyResult::OK;
+}
+
+static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
+{
+ if (!lookupType.isValid()) {
+ // If type is invalid, then the calling code depends on the lookup
+ // to be set up in order to query the type, via lookupResultMetaType.
+ // We cannot verify the type in this case.
+ } else if ((lookupType.flags() & QMetaType::IsQmlList)
+ && (propertyType.flags() & QMetaType::IsQmlList)) {
+ // We want to check the value types here, but we cannot easily do it.
+ // Internally those are all QObject* lists, though.
+ } else if (lookupType.flags() & QMetaType::PointerToQObject) {
+ // We accept any base class as type, too
+
+ const QMetaObject *typeMetaObject = lookupType.metaObject();
+ const QMetaObject *foundMetaObject = propertyType.metaObject();
+ if (!foundMetaObject)
+ foundMetaObject = QQmlMetaType::metaObjectForType(propertyType).metaObject();
+
+ while (foundMetaObject && foundMetaObject != typeMetaObject)
+ foundMetaObject = foundMetaObject->superClass();
+
+ if (!foundMetaObject)
+ return false;
+ } else if (propertyType != lookupType) {
+ return false;
+ }
+ return true;
+}
+
+enum class ObjectLookupResult {
+ Failure,
+ Object,
+ Fallback
+};
+
+static ObjectLookupResult initObjectLookup(
const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
{
QV4::Scope scope(aotContext->engine->handle());
@@ -855,7 +954,7 @@ static bool initObjectLookup(
QQmlData *ddata = QQmlData::get(object, true);
Q_ASSERT(ddata);
if (ddata->isQueuedForDeletion)
- return false;
+ return ObjectLookupResult::Failure;
QQmlPropertyData *property;
if (!ddata->propertyCache) {
@@ -865,41 +964,36 @@ static bool initObjectLookup(
name.getPointer(), object, aotContext->qmlContext);
}
- if (!property)
- return false;
-
- const QMetaType propType = property->propType();
- if (!type.isValid()) {
- // If type is invalid, then the calling code depends on the lookup
- // to be set up in order to query the type, via lookupResultMetaType.
- // We cannot verify the type in this case.
- } else if ((type.flags() & QMetaType::IsQmlList) && (propType.flags() & QMetaType::IsQmlList)) {
- // We want to check the value types here, but we cannot easily do it.
- // Internally those are all QObject* lists, though.
- } else if (type.flags() & QMetaType::PointerToQObject) {
- // We accept any base class as type, too
-
- const QMetaObject *typeMetaObject = type.metaObject();
- const QMetaObject *foundMetaObject = propType.metaObject();
- if (!foundMetaObject)
- foundMetaObject = QQmlMetaType::metaObjectForType(propType).metaObject();
-
- while (foundMetaObject) {
- if (foundMetaObject == typeMetaObject)
- break;
- foundMetaObject = foundMetaObject->superClass();
- }
-
- if (!foundMetaObject)
- return false;
- } else if (propType != type) {
- return false;
+ if (!property) {
+ const QMetaObject *metaObject = object->metaObject();
+ if (!metaObject)
+ return ObjectLookupResult::Failure;
+
+ const int coreIndex = metaObject->indexOfProperty(
+ name->toQStringNoThrow().toUtf8().constData());
+ if (coreIndex < 0)
+ return ObjectLookupResult::Failure;
+
+ const QMetaProperty property = metaObject->property(coreIndex);
+ if (!isTypeCompatible(type, property.metaType()))
+ return ObjectLookupResult::Failure;
+
+ l->releasePropertyCache();
+ // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
+ l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
+ l->qobjectFallbackLookup.coreIndex = coreIndex;
+ l->qobjectFallbackLookup.notifyIndex = property.notifySignalIndex();
+ l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
+ return ObjectLookupResult::Fallback;
}
+ if (!isTypeCompatible(type, property->propType()))
+ return ObjectLookupResult::Failure;
+
Q_ASSERT(ddata->propertyCache);
QV4::setupQObjectLookup(l, ddata, property);
- return true;
+ return ObjectLookupResult::Object;
}
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@@ -935,29 +1029,47 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
return false;
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty
- && l->getter != QV4::Lookup::getterQObject) {
- return false;
+ if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::Lookup::getterQObject) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(object, property->coreIndex());
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
- QQmlData::flushPendingBinding(object, property->coreIndex());
- captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ if (l->getter == QV4::Lookup::getterFallback) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(object, coreIndex);
+ captureFallbackProperty(
+ object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+
return true;
}
bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty
- && l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupContextObjectProperty) {
- return false;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
- QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
- captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
- return true;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
+ captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+ return false;
}
QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
@@ -978,6 +1090,14 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
|| l->getter == QV4::QObjectWrapper::lookupAttached) {
return QMetaType::fromType<QObject *>();
+ } else if (l->getter == QV4::Lookup::getterFallback
+ || l->setter == QV4::Lookup::setterFallback
+ || l->qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ return metaObject->property(coreIndex).metaType();
}
return QMetaType();
}
@@ -991,9 +1111,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
QV4::Lookup l;
l.clear();
l.nameIndex = nameIndex;
- if (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
-
- ObjectPropertyResult storeResult;
+ ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
+ switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::Object: {
const QMetaType propType = l.qobjectLookup.propertyData->propType();
if (type == propType) {
storeResult = storeObjectProperty(&l, qmlScopeObject, value);
@@ -1003,20 +1123,38 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
- switch (storeResult) {
- case ObjectPropertyResult::NeedsInit:
- engine->handle()->throwTypeError();
- break;
- case ObjectPropertyResult::Deleted:
- engine->handle()->throwTypeError(
- QStringLiteral("Value is null and could not be converted to an object"));
- break;
- case ObjectPropertyResult::OK:
- break;
- }
l.qobjectLookup.propertyCache->release();
- } else {
+ break;
+ }
+ case ObjectLookupResult::Fallback: {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
+ const QMetaType propType
+ = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
+ if (type == propType) {
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
+ } else {
+ QVariant var(propType);
+ propType.convert(type, value, propType, var.data());
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
+ }
+ break;
+ }
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ return;
+ }
+
+ switch (storeResult) {
+ case ObjectPropertyResult::NeedsInit:
engine->handle()->throwTypeError();
+ break;
+ case ObjectPropertyResult::Deleted:
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ break;
+ case ObjectPropertyResult::OK:
+ break;
}
}
@@ -1181,10 +1319,16 @@ void AOTCompiledContext::initLoadGlobalLookup(uint index) const
bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, qmlScopeObject, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::NeedsInit:
return false;
case ObjectPropertyResult::Deleted:
@@ -1205,12 +1349,22 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
QV4::ExecutionEngine *v4 = engine->handle();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (v4->hasException)
+ if (v4->hasException) {
amendException(v4);
- else if (initObjectLookup(this, l, qmlScopeObject, type))
+ return;
+ }
+
+ switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
+ break;
+ case ObjectLookupResult::Failure:
v4->throwTypeError();
+ break;
+ }
}
bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const
@@ -1351,10 +1505,15 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
if (!object)
return doThrow();
- if (l->getter != QV4::Lookup::getterQObject)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = loadObjectProperty(l, object, target, qmlContext);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = loadFallbackProperty(l, object, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, object, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1374,10 +1533,17 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
l->getter = QV4::Lookup::getterQObject;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->getter = QV4::Lookup::getterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
@@ -1445,10 +1611,15 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
return doThrow();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->setter != QV4::Lookup::setterQObject)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->setter == QV4::Lookup::setterQObject)
+ result = storeObjectProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallback)
+ result = storeFallbackProperty(l, object, value);
+ else
return false;
- switch (storeObjectProperty(l, object, value)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1468,10 +1639,17 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
l->setter = QV4::Lookup::setterQObject;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->setter = QV4::Lookup::setterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 37475dd2ee..565e0a085d 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -250,6 +250,8 @@ The following functions are also on the Qt object.
\qmlproperty object Qt::inputMethod
\since 5.0
+ It is the same as the \l InputMethod singleton.
+
The \c inputMethod object allows access to application's QInputMethod object
and all its properties and slots. See the QInputMethod documentation for
further details.
@@ -260,28 +262,12 @@ The following functions are also on the Qt object.
\since 5.5
The \c styleHints object provides platform-specific style hints and settings.
- See the QStyleHints documentation for further details.
-
- \note The \c styleHints object is only available when using the Qt Quick module.
-
- The following example uses the \c styleHints object to determine whether an
- item should gain focus on mouse press or touch release:
- \code
- import QtQuick 2.4
+ See the \l QStyleHints documentation for further details.
- MouseArea {
- id: button
+ You should access StyleHints via \l Application::styleHints instead.
+ See \l StyleHints for more details.
- onPressed: {
- if (!Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- onReleased: {
- if (Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- }
- \endcode
+ \note The \c styleHints object is only available when using the Qt Quick module.
*/
/*!
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index e70dae5de8..0d8eef6aca 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -40,6 +40,9 @@
#ifndef QQMLCOMPONENT_H
#define QQMLCOMPONENT_H
+#include <QtCore/qvariant.h>
+#include <QtCore/qmap.h>
+
#include <QtQml/qqml.h>
#include <QtQml/qqmlerror.h>
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h
index 7d759dfa82..932d917499 100644
--- a/src/qml/qml/qqmlcontextdata_p.h
+++ b/src/qml/qml/qqmlcontextdata_p.h
@@ -320,9 +320,8 @@ private:
ObjectWasSet
};
- inline ContextGuard() : m_context(nullptr) {}
+ inline ContextGuard() : QQmlGuard<QObject>(&ContextGuard::objectDestroyedImpl, nullptr), m_context(nullptr) {}
inline ContextGuard &operator=(QObject *obj);
- inline void objectDestroyed(QObject *) override;
inline bool wasSet() const;
@@ -333,6 +332,7 @@ private:
}
private:
+ inline static void objectDestroyedImpl(QQmlGuardImpl *);
// Not refcounted, as it always belongs to the QQmlContextData.
QTaggedPointer<QQmlContextData, Tag> m_context;
QQmlNotifier m_bindings;
@@ -459,11 +459,12 @@ QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject
return *this;
}
-void QQmlContextData::ContextGuard::objectDestroyed(QObject *)
+ void QQmlContextData::ContextGuard::objectDestroyedImpl(QQmlGuardImpl *impl)
{
- if (QObject *contextObject = m_context->contextObject()) {
+ auto This = static_cast<QQmlContextData::ContextGuard *>(impl);
+ if (QObject *contextObject = This->m_context->contextObject()) {
if (!QObjectPrivate::get(contextObject)->wasDeleted)
- m_bindings.notify();
+ This->m_bindings.notify();
}
}
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index a88cea55cd..99697f8e1a 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -470,19 +470,32 @@ void QQmlEnginePrivate::init()
// required for the Compiler.
qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
+ qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<int>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<double>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QString>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>("QML", 1);
+
+ // No need to specifically register those.
+ static_assert(std::is_same_v<QStringList, QList<QString>>);
+ static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
+
+ qRegisterMetaType<QVariant>();
+ qRegisterMetaType<QQmlScriptString>();
+ qRegisterMetaType<QJSValue>();
+ qRegisterMetaType<QQmlComponent::Status>();
+ qRegisterMetaType<QList<QObject*> >();
+ qRegisterMetaType<QQmlBinding*>();
QQmlData::init();
baseModulesUninitialized = false;
}
- qRegisterMetaType<QVariant>();
- qRegisterMetaType<QQmlScriptString>();
- qRegisterMetaType<QJSValue>();
- qRegisterMetaType<QQmlComponent::Status>();
- qRegisterMetaType<QList<QObject*> >();
- qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlBinding*>();
-
q->handle()->setQmlEngine(q);
rootContext = new QQmlContext(q,true);
@@ -1310,9 +1323,10 @@ void QQmlData::destroyed(QObject *object)
ownContext.reset();
while (guards) {
- QQmlGuard<QObject> *guard = static_cast<QQmlGuard<QObject> *>(guards);
- *guard = (QObject *)nullptr;
- guard->objectDestroyed(object);
+ auto *guard = guards;
+ guard->setObject(nullptr);
+ if (guard->objectDestroyed)
+ guard->objectDestroyed(guard);
}
disconnectNotifiers();
@@ -1945,6 +1959,8 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
\sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
*/
+void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
+
QT_END_NAMESPACE
#include "moc_qqmlengine.cpp"
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index e36af7f133..9c167b684b 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE
class QQmlGuardImpl
{
public:
+ using ObjectDestroyedFn = void(*)(QQmlGuardImpl *);
+
inline QQmlGuardImpl();
inline QQmlGuardImpl(QObject *);
inline QQmlGuardImpl(const QQmlGuardImpl &);
@@ -67,70 +69,86 @@ public:
QObject *o = nullptr;
QQmlGuardImpl *next = nullptr;
QQmlGuardImpl **prev = nullptr;
+ ObjectDestroyedFn objectDestroyed = nullptr;
inline void addGuard();
inline void remGuard();
+
+ inline void setObject(QObject *g);
+ bool isNull() const noexcept { return !o; }
};
class QObject;
template<class T>
-class QQmlGuard : private QQmlGuardImpl
+class QQmlGuard : protected QQmlGuardImpl
{
friend class QQmlData;
public:
inline QQmlGuard();
+ inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
inline QQmlGuard(T *);
inline QQmlGuard(const QQmlGuard<T> &);
- inline virtual ~QQmlGuard();
inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o);
inline QQmlGuard<T> &operator=(T *);
- inline T *object() const;
- inline void setObject(T *g);
-
- inline bool isNull() const
- { return !o; }
+ T *object() const noexcept { return static_cast<T *>(o); }
+ void setObject(T *g) { QQmlGuardImpl::setObject(g); }
- inline T* operator->() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
- inline T& operator*() const
- { return *static_cast<T*>(const_cast<QObject*>(o)); }
- inline operator T*() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
- inline T* data() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
+ using QQmlGuardImpl::isNull;
-protected:
- virtual void objectDestroyed(T *) {}
+ T *operator->() const noexcept { return object(); }
+ T &operator*() const { return *object(); }
+ operator T *() const noexcept { return object(); }
+ T *data() const noexcept { return object(); }
};
+/* used in QQmlStrongJSQObjectReference to indicate that the
+ * object has JS ownership
+ * We save it in objectDestroyFn to save space
+ * (implemented in qqmlengine.cpp)
+ */
+void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
+
template <typename T>
-class QQmlStrongJSQObjectReference : public QQmlGuard<T>
+class QQmlStrongJSQObjectReference : protected QQmlGuardImpl
{
public:
- void setObject(T *o, QObject *parent) {
- T *old = this->object();
- if (o == old)
+ T *object() const noexcept { return static_cast<T *>(o); }
+
+ using QQmlGuardImpl::isNull;
+
+ T *operator->() const noexcept { return object(); }
+ T &operator*() const { return *object(); }
+ operator T *() const noexcept { return object(); }
+ T *data() const noexcept { return object(); }
+
+ void setObject(T *obj, QObject *parent) {
+ T *old = object();
+ if (obj == old)
return;
- if (m_jsOwnership && old && old->parent() == parent)
+ if (hasJsOwnership() && old && old->parent() == parent)
QQml_setParent_noEvent(old, nullptr);
- this->QQmlGuard<T>::operator=(o);
+ QQmlGuardImpl::setObject(obj);
- if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) {
- m_jsOwnership = true;
- QQml_setParent_noEvent(o, parent);
+ if (obj && !obj->parent() && !QQmlData::keepAliveDuringGarbageCollection(obj)) {
+ setJsOwnership(true);
+ QQml_setParent_noEvent(obj, parent);
} else {
- m_jsOwnership = false;
+ setJsOwnership(false);
}
}
private:
- using QQmlGuard<T>::setObject;
- using QQmlGuard<T>::operator=;
- bool m_jsOwnership = false;
+ bool hasJsOwnership() {
+ return objectDestroyed == hasJsOwnershipIndicator;
+ }
+
+ void setJsOwnership(bool itHasOwnership) {
+ objectDestroyed = itHasOwnership ? hasJsOwnershipIndicator : nullptr;
+ }
};
QT_END_NAMESPACE
@@ -149,8 +167,15 @@ QQmlGuardImpl::QQmlGuardImpl(QObject *g)
if (o) addGuard();
}
+/*
+ \internal
+ Copying a QQmlGuardImpl leaves the old one in the intrinsic linked list of guards.
+ The fresh copy does not contain the list pointer of the existing guard; instead
+ only the object and objectDestroyed pointers are copied, and if there is an object
+ we add the new guard to the object's list of guards.
+ */
QQmlGuardImpl::QQmlGuardImpl(const QQmlGuardImpl &g)
-: o(g.o)
+: o(g.o), objectDestroyed(g.objectDestroyed)
{
if (o) addGuard();
}
@@ -191,25 +216,28 @@ QQmlGuard<T>::QQmlGuard()
}
template<class T>
-QQmlGuard<T>::QQmlGuard(T *g)
-: QQmlGuardImpl(g)
+QQmlGuard<T>::QQmlGuard(ObjectDestroyedFn objDestroyed, T *obj)
+ : QQmlGuardImpl(obj)
{
+ objectDestroyed = objDestroyed;
}
template<class T>
-QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g)
+QQmlGuard<T>::QQmlGuard(T *g)
: QQmlGuardImpl(g)
{
}
template<class T>
-QQmlGuard<T>::~QQmlGuard()
+QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g)
+: QQmlGuardImpl(g)
{
}
template<class T>
QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g)
{
+ objectDestroyed = g.objectDestroyed;
setObject(g.object());
return *this;
}
@@ -217,18 +245,15 @@ QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g)
template<class T>
QQmlGuard<T> &QQmlGuard<T>::operator=(T *g)
{
+ /* this does not touch objectDestroyed, as operator= is only a convenience
+ * for setObject. All logic involving objectDestroyed is (sub-)class specific
+ * and remains unaffected.
+ */
setObject(g);
return *this;
}
-template<class T>
-T *QQmlGuard<T>::object() const
-{
- return static_cast<T *>(o);
-}
-
-template<class T>
-void QQmlGuard<T>::setObject(T *g)
+void QQmlGuardImpl::setObject(QObject *g)
{
if (g != o) {
if (prev) remGuard();
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index f51fc6dc22..2eb756be1f 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1397,6 +1397,18 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
const QString& uri, const QString &prefix, QTypeRevision version, uint flags,
QQmlImportDatabase *database, QList<QQmlError> *errors)
{
+ if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
+ QQmlError error;
+ const QString fix = uri.startsWith(Slash) ? QLatin1String("file:") + uri
+ : QLatin1String("qrc") + uri;
+ error.setDescription(QQmlImportDatabase::tr(
+ "\"%1\" is not a valid import URL. "
+ "You can pass relative paths or URLs with schema, but not "
+ "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
Q_ASSERT(errors);
QQmlImportNamespace *nameSpace = importNamespace(prefix);
@@ -1670,7 +1682,7 @@ void QQmlImports::setDesignerSupportRequired(bool b)
designerSupportRequired = b;
}
-static QStringList parseEnvImportPath(const QString &envImportPath)
+static QStringList parseEnvPath(const QString &envImportPath)
{
if (QDir::listSeparator() == u':') {
// Double colons are interpreted as separator + resource path.
@@ -1710,7 +1722,7 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
auto addEnvImportPath = [this](const char *var) {
if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) {
- const QStringList paths = parseEnvImportPath(qEnvironmentVariable(var));
+ const QStringList paths = parseEnvPath(qEnvironmentVariable(var));
for (int ii = paths.count() - 1; ii >= 0; --ii)
addImportPath(paths.at(ii));
}
@@ -1722,15 +1734,19 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
addImportPath(QCoreApplication::applicationDirPath());
+
+ auto addEnvPluginPath = [this](const char *var) {
+ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) {
+ const QStringList paths = parseEnvPath(qEnvironmentVariable(var));
+ for (int ii = paths.count() - 1; ii >= 0; --ii)
+ addPluginPath(paths.at(ii));
+ }
+ };
+
+ addEnvPluginPath("QML_PLUGIN_PATH");
#if defined(Q_OS_ANDROID)
addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml"));
- if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) {
- const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH");
- QLatin1Char pathSep(':');
- QStringList paths = envImportPath.split(pathSep, Qt::SkipEmptyParts);
- for (int ii = paths.count() - 1; ii >= 0; --ii)
- addPluginPath(paths.at(ii));
- }
+ addEnvPluginPath("QT_BUNDLED_LIBS_PATH");
#elif defined(Q_OS_MACOS)
// Add the main bundle's Resources/qml directory as an import path, so that QML modules are
// found successfully when running the app from its build dir.
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 3589dce3f0..1794a27a9d 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -703,7 +703,7 @@ ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const QV4::FunctionObject
LOCALE_STRING_PROPERTY(name)
LOCALE_STRING_PROPERTY(nativeLanguageName)
-LOCALE_STRING_PROPERTY(nativeCountryName)
+QT_IGNORE_DEPRECATIONS(LOCALE_STRING_PROPERTY(nativeCountryName))
LOCALE_STRING_PROPERTY(decimalPoint)
LOCALE_STRING_PROPERTY(groupSeparator)
LOCALE_STRING_PROPERTY(percent)
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index fb0e2af578..125693f3b6 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -47,10 +47,6 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qv4executablecompilationunit_p.h>
-#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
-#endif
-
#include <QtCore/qcoreapplication.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
@@ -257,20 +253,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_PROPERTY
- for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
- QMetaProperty property = mo->property(ii);
-
- int otherIndex = ignoreEnd->indexOfProperty(property.name());
- if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
- builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void"));
- // Skip
- } else {
- builder.addProperty(property);
- }
- }
-
- // Clone Q_METHODS
+ // Clone Q_METHODS - do this first to avoid duplicating the notify signals.
for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) {
QMetaMethod method = mo->method(ii);
@@ -294,6 +277,19 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
m.setAccess(QMetaMethod::Private);
}
+ // Clone Q_PROPERTY
+ for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
+ QMetaProperty property = mo->property(ii);
+
+ int otherIndex = ignoreEnd->indexOfProperty(property.name());
+ if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
+ builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void"));
+ // Skip
+ } else {
+ builder.addProperty(property);
+ }
+ }
+
// Clone Q_ENUMS
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
@@ -1718,17 +1714,7 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
case QMetaType::QEasingCurve:
return &QQmlEasingValueType::staticMetaObject;
#endif
-#if QT_CONFIG(qml_itemmodel)
- case QMetaType::QModelIndex:
- return &QQmlModelIndexValueType::staticMetaObject;
- case QMetaType::QPersistentModelIndex:
- return &QQmlPersistentModelIndexValueType::staticMetaObject;
-#endif
default:
-#if QT_CONFIG(qml_itemmodel)
- if (metaType == QMetaType::fromType<QItemSelectionRange>())
- return &QQmlItemSelectionRangeValueType::staticMetaObject;
-#endif
break;
}
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index ae227109bb..8b905c2351 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -42,6 +42,7 @@
#include <private/qqmldata_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <qdebug.h>
+#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp
index 7723630e1d..fbb691c48e 100644
--- a/src/qml/qml/qqmlpluginimporter.cpp
+++ b/src/qml/qml/qqmlpluginimporter.cpp
@@ -507,7 +507,7 @@ bool QQmlPluginImporter::populatePluginDataVector(QVector<StaticPluginData> &res
QObject *instance = plugin.instance();
if (qobject_cast<QQmlEngineExtensionPlugin *>(instance)
|| qobject_cast<QQmlExtensionPlugin *>(instance)) {
- QJsonArray metaTagsUriList = plugin.metaData().value(
+ const QJsonArray metaTagsUriList = plugin.metaData().value(
QStringLiteral("uri")).toArray();
if (metaTagsUriList.isEmpty()) {
if (errors) {
@@ -523,7 +523,7 @@ bool QQmlPluginImporter::populatePluginDataVector(QVector<StaticPluginData> &res
return false;
}
// A plugin can be set up to handle multiple URIs, so go through the list:
- for (const QJsonValueRef metaTagUri : metaTagsUriList) {
+ for (const QJsonValueConstRef metaTagUri : metaTagsUriList) {
if (versionUris.contains(metaTagUri.toString())) {
result.append({ plugin, metaTagsUriList });
break;
@@ -585,8 +585,8 @@ QTypeRevision QQmlPluginImporter::importPlugins() {
return QTypeRevision();
for (const QString &versionUri : versionUris) {
- for (StaticPluginData &pair : pluginPairs) {
- for (QJsonValueRef metaTagUri : pair.uriList) {
+ for (const StaticPluginData &pair : qAsConst(pluginPairs)) {
+ for (const QJsonValueConstRef metaTagUri : pair.uriList) {
if (versionUri == metaTagUri.toString()) {
staticPluginsFound++;
QObject *instance = pair.plugin.instance();
diff --git a/src/qml/qml/qqmlproperty.h b/src/qml/qml/qqmlproperty.h
index 4ab84eb940..dbffce9437 100644
--- a/src/qml/qml/qqmlproperty.h
+++ b/src/qml/qml/qqmlproperty.h
@@ -40,6 +40,8 @@
#ifndef QQMLPROPERTY_H
#define QQMLPROPERTY_H
+#include <QtCore/qstring.h>
+#include <QtCore/qhashfunctions.h>
#include <QtQml/qtqmlglobal.h>
#include <QtCore/qmetaobject.h>
#include <QtQml/qqmlregistration.h>
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 66e83c1655..035098d4c5 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1256,35 +1256,41 @@ bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &m
return true;
}
-QByteArray QQmlPropertyCache::checksum(bool *ok)
+QByteArray QQmlPropertyCache::checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const
{
- if (!_checksum.isEmpty()) {
+ auto it = checksums->constFind(quintptr(this));
+ if (it != checksums->constEnd()) {
*ok = true;
- return _checksum;
+ return *it;
}
// Generate a checksum on the meta-object data only on C++ types.
if (!_metaObject || _metaObject.isShared()) {
*ok = false;
- return _checksum;
+ return QByteArray();
}
QCryptographicHash hash(QCryptographicHash::Md5);
if (_parent) {
- hash.addData(_parent->checksum(ok));
+ hash.addData(_parent->checksum(checksums, ok));
if (!*ok)
return QByteArray();
}
- if (!addToHash(hash, *createMetaObject())) {
+ if (!addToHash(hash, *_metaObject)) {
*ok = false;
return QByteArray();
}
- _checksum = hash.result();
- *ok = !_checksum.isEmpty();
- return _checksum;
+ const QByteArray result = hash.result();
+ if (result.isEmpty()) {
+ *ok = false;
+ } else {
+ *ok = true;
+ checksums->insert(quintptr(this), result);
+ }
+ return result;
}
/*! \internal
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 68830ba716..4c10de7823 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -247,7 +247,7 @@ public:
static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
- QByteArray checksum(bool *ok);
+ QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const;
QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; }
void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; }
@@ -333,7 +333,6 @@ private:
QByteArray _listPropertyAssignBehavior;
QString _defaultPropertyName;
QQmlPropertyCacheMethodArguments *argumentsCache = nullptr;
- QByteArray _checksum;
int methodIndexCacheStart = 0;
int signalHandlerIndexCacheStart = 0;
int _jsFactoryMethodIndex = -1;
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index aa3be1cabf..448bae3cc5 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -253,7 +253,8 @@ void QQmlTypeData::createTypeAndPropertyCaches(
}
static bool addTypeReferenceChecksumsToHash(
- const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash)
+ const QList<QQmlTypeData::TypeReference> &typeRefs,
+ QHash<quintptr, QByteArray> *checksums, QCryptographicHash *hash)
{
for (const auto &typeRef: typeRefs) {
if (typeRef.typeData) {
@@ -262,7 +263,7 @@ static bool addTypeReferenceChecksumsToHash(
} else if (typeRef.type.isValid()) {
const auto propertyCache = QQmlMetaType::propertyCache(typeRef.type.metaObject());
bool ok = false;
- hash->addData(propertyCache->checksum(&ok));
+ hash->addData(propertyCache->checksum(checksums, &ok));
if (!ok)
return false;
}
@@ -420,8 +421,9 @@ void QQmlTypeData::done()
const auto dependencyHasher = [&resolvedTypeCache, this]() {
QCryptographicHash hash(QCryptographicHash::Md5);
- return (resolvedTypeCache.addToHash(&hash)
- && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash))
+ return (resolvedTypeCache.addToHash(&hash, typeLoader()->checksumCache())
+ && ::addTypeReferenceChecksumsToHash(
+ m_compositeSingletons, typeLoader()->checksumCache(), &hash))
? hash.result()
: QByteArray();
};
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 59c9f858f6..b41f15a7c2 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1220,6 +1220,7 @@ void QQmlTypeLoader::clearCache()
m_qmldirCache.clear();
m_importDirCache.clear();
m_importQmlDirCache.clear();
+ m_checksumCache.clear();
QQmlMetaType::freeUnusedTypesAndCaches();
}
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index eb6e549911..10f96df735 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -78,6 +78,7 @@ class Q_QML_PRIVATE_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
+ using ChecksumCache = QHash<quintptr, QByteArray>;
enum Mode { PreferSynchronous, Asynchronous, Synchronous };
class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob
@@ -141,6 +142,8 @@ public:
~QQmlTypeLoader();
QQmlImportDatabase *importDatabase() const;
+ ChecksumCache *checksumCache() { return &m_checksumCache; }
+ const ChecksumCache *checksumCache() const { return &m_checksumCache; }
static QUrl normalize(const QUrl &unNormalizedUrl);
@@ -247,6 +250,7 @@ private:
QmldirCache m_qmldirCache;
ImportDirCache m_importDirCache;
ImportQmlDirCache m_importQmlDirCache;
+ ChecksumCache m_checksumCache;
template<typename Loader>
void doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode);
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 93abe3c83a..ef41d81868 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -165,31 +165,28 @@ static void list_removeLast(QQmlListProperty<QObject> *prop)
}
QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr()
- : QQmlGuard<QObject>(nullptr), m_target(nullptr), m_index(-1)
+ : QQmlGuard<QObject>(QQmlVMEVariantQObjectPtr::objectDestroyedImpl, nullptr), m_target(nullptr), m_index(-1)
{
}
-QQmlVMEVariantQObjectPtr::~QQmlVMEVariantQObjectPtr()
+void QQmlVMEVariantQObjectPtr::objectDestroyedImpl(QQmlGuardImpl *guard)
{
-}
-
-void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
-{
- if (!m_target || QQmlData::wasDeleted(m_target->object))
+ auto This = static_cast<QQmlVMEVariantQObjectPtr *>(guard);
+ if (!This->m_target || QQmlData::wasDeleted(This->m_target->object))
return;
- if (m_index >= 0) {
- QV4::ExecutionEngine *v4 = m_target->propertyAndMethodStorage.engine();
+ if (This->m_index >= 0) {
+ QV4::ExecutionEngine *v4 = This->m_target->propertyAndMethodStorage.engine();
if (v4) {
QV4::Scope scope(v4);
- QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
+ QV4::Scoped<QV4::MemberData> sp(scope, This->m_target->propertyAndMethodStorage.value());
if (sp) {
- QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index };
+ QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + This->m_index };
index.set(v4, QV4::Value::nullValue());
}
}
- m_target->activate(m_target->object, m_target->methodOffset() + m_index, nullptr);
+ This->m_target->activate(This->m_target->object, This->m_target->methodOffset() + This->m_index, nullptr);
}
}
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index d8ae670597..e7ddf4d144 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -79,13 +79,14 @@ class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
public:
inline QQmlVMEVariantQObjectPtr();
- inline ~QQmlVMEVariantQObjectPtr() override;
- inline void objectDestroyed(QObject *) override;
inline void setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index);
QQmlVMEMetaObject *m_target;
int m_index;
+
+private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard);
};
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index bb9036582e..3bc960291a 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -241,6 +241,23 @@ bool QQmlDirParser::parse(const QString &source)
"not %1.").arg(sections[1]));
continue;
}
+ } else if (sections[0] == QLatin1String("default")) {
+ if (sectionCount < 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("default directive requires further "
+ "arguments, but none were provided."));
+ continue;
+ }
+ if (sections[1] == QLatin1String("import")) {
+ if (!readImport(sections + 1, sectionCount - 1,
+ Import::Flags({ Import::Optional, Import::OptionalDefault })))
+ continue;
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("only optional imports can have a a defaultl, "
+ "not %1.")
+ .arg(sections[1]));
+ }
} else if (sections[0] == QLatin1String("classname")) {
if (sectionCount < 2) {
reportError(lineNumber, 0,
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index 6367cae141..1a9fa6ff2b 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -134,9 +134,11 @@ public:
struct Import
{
enum Flag {
- Default = 0x0,
- Auto = 0x1, // forward the version of the importing module
- Optional = 0x2 // is not automatically imported but only a tooling hint
+ Default = 0x0,
+ Auto = 0x1, // forward the version of the importing module
+ Optional = 0x2, // is not automatically imported but only a tooling hint
+ OptionalDefault =
+ 0x4, // tooling hint only, denotes this entry should be imported by tooling
};
Q_DECLARE_FLAGS(Flags, Flag)
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 25a0d130a6..59d8bf8a98 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -8,7 +8,8 @@ qt_internal_add_module(QmlCompilerPrivate
SOURCES
qcoloroutput_p.h qcoloroutput.cpp
qdeferredpointer_p.h
- qqmljsannotation_p.h qqmljsannotation.cpp
+ qqmljsannotation.cpp qqmljsannotation_p.h
+ qqmljsbasicblocks.cpp qqmljsbasicblocks_p.h
qqmljscodegenerator.cpp qqmljscodegenerator_p.h
qqmljscompilepass_p.h
qqmljscompiler.cpp qqmljscompiler_p.h
@@ -17,7 +18,7 @@ qt_internal_add_module(QmlCompilerPrivate
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
qqmljslogger_p.h qqmljslogger.cpp
- qqmljsmetatypes_p.h
+ qqmljsmetatypes_p.h qqmljsmetatypes.cpp
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
qqmljsscope.cpp qqmljsscope_p.h
diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h
index 9bf67fb27f..2da17b3dd9 100644
--- a/src/qmlcompiler/qdeferredpointer_p.h
+++ b/src/qmlcompiler/qdeferredpointer_p.h
@@ -120,6 +120,26 @@ public:
return !(a == b);
}
+ friend bool operator<(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data < b.m_data;
+ }
+
+ friend bool operator<=(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data <= b.m_data;
+ }
+
+ friend bool operator>(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data > b.m_data;
+ }
+
+ friend bool operator>=(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data >= b.m_data;
+ }
+
template <typename U>
friend bool operator==(const QDeferredSharedPointer &a, const QSharedPointer<U> &b)
{
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
new file mode 100644
index 0000000000..ebd0458897
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmljsbasicblocks_p.h"
+
+QT_BEGIN_NAMESPACE
+
+template<typename Container>
+void deduplicate(Container &container)
+{
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+}
+
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
+ const Function *function,
+ const InstructionAnnotations &annotations)
+{
+ m_annotations = annotations;
+ m_basicBlocks.insert_or_assign(0, BasicBlock());
+
+ const QByteArray byteCode = function->code;
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ if (m_hadBackJumps) {
+ // We may have missed some connections between basic blocks if there were back jumps.
+ // Fill them in via a second pass.
+ reset();
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
+ deduplicate(it->second.jumpOrigins);
+ }
+
+ populateBasicBlocks();
+ populateReaderLocations();
+ adjustTypes();
+ return std::move(m_annotations);
+}
+
+QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type)
+{
+ auto it = m_basicBlocks.find(currentInstructionOffset());
+ if (it != m_basicBlocks.end()) {
+ if (!m_skipUntilNextLabel && it != m_basicBlocks.begin())
+ it->second.jumpOrigins.append((--it)->first);
+ m_skipUntilNextLabel = false;
+ } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
+ return SkipInstruction;
+ }
+
+ return ProcessInstruction;
+}
+
+void QQmlJSBasicBlocks::endInstruction(QV4::Moth::Instr::Type)
+{
+}
+
+void QQmlJSBasicBlocks::generate_Jump(int offset)
+{
+ processJump(offset, Unconditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpTrue(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpFalse(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNoException(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_Ret()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::generate_ThrowException()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
+{
+ if (offset < 0)
+ m_hadBackJumps = true;
+ const int jumpTarget = absoluteOffset(offset);
+ Q_ASSERT(!m_basicBlocks.isEmpty());
+ auto currentBlock = m_basicBlocks.lower_bound(currentInstructionOffset());
+ if (currentBlock == m_basicBlocks.end() || currentBlock->first != currentInstructionOffset())
+ --currentBlock;
+ currentBlock->second.jumpTarget = jumpTarget;
+ currentBlock->second.jumpIsUnconditional = (mode == Unconditional);
+ m_basicBlocks[jumpTarget].jumpOrigins.append(currentInstructionOffset());
+ if (mode == Unconditional)
+ m_skipUntilNextLabel = true;
+ else
+ m_basicBlocks[nextInstructionOffset()].jumpOrigins.append(currentInstructionOffset());
+}
+
+template<typename ContainerA, typename ContainerB>
+static bool containsAny(const ContainerA &container, const ContainerB &elements)
+{
+ for (const auto &element : elements) {
+ if (container.contains(element))
+ return true;
+ }
+ return false;
+}
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
+class NewFlatMap
+{
+public:
+ using OriginalFlatMap = QFlatMap<Key, T, Compare, KeyContainer, MappedContainer>;
+
+ void appendOrdered(const typename OriginalFlatMap::iterator &i) {
+ keys.append(i.key());
+ values.append(i.value());
+ }
+
+ OriginalFlatMap take() {
+ OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
+ keys.clear();
+ values.clear();
+ return result;
+ }
+
+private:
+ typename OriginalFlatMap::key_container_type keys;
+ typename OriginalFlatMap::mapped_container_type values;
+};
+
+void QQmlJSBasicBlocks::populateReaderLocations()
+{
+ using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
+
+ bool erasedReaders = false;
+ auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
+ auto reader = m_readerLocations.find(it.key());
+ if (reader != m_readerLocations.end() && reader->readers.isEmpty()) {
+
+ // void the output, rather than deleting it. We still need its variant.
+ m_typeResolver->adjustTrackedType(
+ it->second.changedRegister.storedType(),
+ m_typeResolver->voidType());
+ m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(it->second.changedRegister),
+ m_typeResolver->voidType());
+ m_readerLocations.erase(reader);
+
+ // If it's not a label and has no side effects, we can drop the instruction.
+ if (!it->second.hasSideEffects && m_basicBlocks.find(it.key()) == m_basicBlocks.end()) {
+ if (!it->second.readRegisters.isEmpty()) {
+ it->second.readRegisters.clear();
+ erasedReaders = true;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ NewInstructionAnnotations newAnnotations;
+ for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
+ writeIt != writeEnd; ++writeIt) {
+ const int writtenRegister = writeIt->second.changedRegisterIndex;
+ if (writtenRegister == InvalidRegister) {
+ newAnnotations.appendOrdered(writeIt);
+ continue;
+ }
+
+ RegisterAccess &access = m_readerLocations[writeIt.key()];
+ if (writeIt->second.changedRegister.isConversion()) {
+ // If it's a conversion, we have to check for all readers of the conversion origins.
+ // This happens at jump targets where different types are merged. A StoreReg or similar
+ // instruction must be optimized out if none of the types it can hold is read anymore.
+ access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
+ } else {
+ access.trackedTypes.append(
+ m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
+ }
+
+ auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
+ if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
+ --blockIt;
+
+ QList<int> blocks = { blockIt->first };
+ QList<int> processedBlocks;
+ bool isFirstBlock = true;
+
+ while (!blocks.isEmpty()) {
+ auto nextBlock = m_basicBlocks.find(blocks.takeLast());
+ auto currentBlock = nextBlock++;
+
+ if (isFirstBlock
+ || containsAny(currentBlock->second.readRegisters, access.trackedTypes)) {
+ const auto blockEnd = (nextBlock == m_basicBlocks.end())
+ ? m_annotations.end()
+ : m_annotations.find(nextBlock->first);
+
+ auto blockInstr = isFirstBlock
+ ? (writeIt + 1)
+ : m_annotations.find(currentBlock->first);
+ for (; blockInstr != blockEnd; ++blockInstr) {
+ for (auto readIt = blockInstr->second.readRegisters.constBegin(),
+ end = blockInstr->second.readRegisters.constEnd();
+ readIt != end; ++readIt) {
+ Q_ASSERT(readIt->second.isConversion());
+ if (containsAny(readIt->second.conversionOrigins(), access.trackedTypes))
+ access.readers[blockInstr.key()] = readIt->second.conversionResult();
+ }
+ }
+ }
+
+ if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()
+ && !processedBlocks.contains(nextBlock->first)) {
+ blocks.append(nextBlock->first);
+ }
+
+ const int jumpTarget = currentBlock->second.jumpTarget;
+ if (jumpTarget != -1 && !processedBlocks.contains(jumpTarget))
+ blocks.append(jumpTarget);
+
+ // We can re-enter the first block from the beginning.
+ // We will then find any reads before the write we're currently examining.
+ if (isFirstBlock)
+ isFirstBlock = false;
+ else
+ processedBlocks.append(currentBlock->first);
+ }
+
+ if (!eraseDeadStore(writeIt))
+ newAnnotations.appendOrdered(writeIt);
+ }
+ m_annotations = newAnnotations.take();
+
+ while (erasedReaders) {
+ erasedReaders = false;
+
+ for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
+ InstructionAnnotation &instruction = it->second;
+ if (instruction.changedRegisterIndex < InvalidRegister) {
+ newAnnotations.appendOrdered(it);
+ continue;
+ }
+
+ auto readers = m_readerLocations.find(it.key());
+ if (readers != m_readerLocations.end()) {
+ for (auto it = readers->readers.begin(); it != readers->readers.end();) {
+ if (m_annotations.contains(it.key()))
+ ++it;
+ else
+ it = readers->readers.erase(it);
+ }
+ }
+
+ if (!eraseDeadStore(it))
+ newAnnotations.appendOrdered(it);
+ }
+
+ m_annotations = newAnnotations.take();
+ }
+}
+
+void QQmlJSBasicBlocks::adjustTypes()
+{
+ using NewVirtualRegisters = NewFlatMap<int, QQmlJSRegisterContent>;
+
+ for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
+ // There is always one first occurrence of any tracked type. Conversions don't change
+ // the type.
+ if (it->trackedTypes.length() != 1)
+ continue;
+
+ m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->readers.values());
+ }
+
+ const auto transformRegister = [&](QQmlJSRegisterContent &content) {
+ m_typeResolver->adjustTrackedType(
+ content.storedType(),
+ m_typeResolver->storedType(m_typeResolver->containedType(content)));
+ };
+
+ NewVirtualRegisters newRegisters;
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
+ if (i->second.changedRegisterIndex != InvalidRegister)
+ transformRegister(i->second.changedRegister);
+ for (auto content : i->second.typeConversions) {
+ QQmlJSScope::ConstPtr conversionResult = content.second.conversionResult();
+ const auto conversionOrigins = content.second.conversionOrigins();
+ QQmlJSScope::ConstPtr newResult;
+ for (const auto &origin : conversionOrigins)
+ newResult = m_typeResolver->merge(newResult, origin);
+ m_typeResolver->adjustTrackedType(conversionResult, newResult);
+ transformRegister(content.second);
+ }
+ i->second.typeConversions = newRegisters.take();
+ }
+}
+
+void QQmlJSBasicBlocks::populateBasicBlocks()
+{
+ for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
+ blockNext != blockEnd;) {
+
+ const auto blockIt = blockNext++;
+ BasicBlock &block = blockIt->second;
+ QList<QQmlJSScope::ConstPtr> writtenRegisters;
+
+ const auto instrEnd = (blockNext == blockEnd)
+ ? m_annotations.end()
+ : m_annotations.find(blockNext->first);
+ for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
+ const InstructionAnnotation &instruction = instrIt->second;
+ for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
+ it != end; ++it) {
+ Q_ASSERT(it->second.isConversion());
+ for (const QQmlJSScope::ConstPtr &origin : it->second.conversionOrigins()) {
+ if (!writtenRegisters.contains(origin))
+ block.readRegisters.append(origin);
+ }
+ }
+
+ // If it's just a renaming, the type has existed in a different register before.
+ if (instruction.changedRegisterIndex != InvalidRegister && !instruction.isRename) {
+ writtenRegisters.append(m_typeResolver->trackedContainedType(
+ instruction.changedRegister));
+ }
+ }
+
+ deduplicate(block.readRegisters);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
new file mode 100644
index 0000000000..76f3216ce8
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLJSBASICBLOCKS_P_H
+#define QQMLJSBASICBLOCKS_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 <private/qqmljscompilepass_p.h>
+#include <private/qflatmap_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlJSBasicBlocks : public QQmlJSCompilePass
+{
+public:
+ struct BasicBlock {
+ QList<int> jumpOrigins;
+ QList<QQmlJSScope::ConstPtr> readRegisters;
+ int jumpTarget = -1;
+ bool jumpIsUnconditional = false;
+ };
+
+ QQmlJSBasicBlocks(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ {
+ }
+
+ ~QQmlJSBasicBlocks() = default;
+
+ InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations);
+
+private:
+ struct RegisterAccess
+ {
+ QList<QQmlJSScope::ConstPtr> trackedTypes;
+ QHash<int, QQmlJSScope::ConstPtr> readers;
+ };
+
+ QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override;
+ void endInstruction(QV4::Moth::Instr::Type type) override;
+
+ void generate_Jump(int offset) override;
+ void generate_JumpTrue(int offset) override;
+ void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
+
+ void generate_Ret() override;
+ void generate_ThrowException() override;
+
+ enum JumpMode { Unconditional, Conditional };
+ void processJump(int offset, JumpMode mode);
+ void populateBasicBlocks();
+ void populateReaderLocations();
+ void adjustTypes();
+
+ InstructionAnnotations m_annotations;
+ QFlatMap<int, BasicBlock> m_basicBlocks;
+ QHash<int, RegisterAccess> m_readerLocations;
+ bool m_skipUntilNextLabel = false;
+ bool m_hadBackJumps = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSBASICBLOCKS_P_H
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index f665b1ae71..f7fa9ba403 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -34,6 +34,7 @@
#include <private/qqmljsscope_p.h>
#include <private/qqmljsutils_p.h>
#include <private/qv4compilerscanfunctions_p.h>
+#include <private/qduplicatetracker_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
@@ -83,13 +84,26 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite())
+ if (!objectType->isComposite()) {
+ if (objectType->internalName() == u"QObject"_qs
+ || objectType->internalName() == u"QQmlComponent"_qs) {
+ return u'&' + objectType->internalName() + u"::staticMetaObject"_qs;
+ }
return metaTypeFromName(objectType) + u".metaObject()"_qs;
+ }
reject(u"retrieving the metaObject of a composite type without using an instance."_qs);
return QString();
}
+static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
+{
+ return !type.isNull()
+ && !resolver->equals(type, resolver->nullType())
+ && !resolver->equals(type, resolver->emptyListType())
+ && !resolver->equals(type, resolver->voidType());
+}
+
QQmlJSAotFunction QQmlJSCodeGenerator::run(
const Function *function, const InstructionAnnotations *annotations,
QQmlJS::DiagnosticMessage *error)
@@ -98,34 +112,41 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
m_function = function;
m_error = error;
- QSet<QString> registerNames;
- for (const InstructionAnnotation &annotation : *m_annotations) {
- for (auto regIt = annotation.registers.constBegin(),
- regEnd = annotation.registers.constEnd();
- regIt != regEnd;
- ++regIt) {
- int registerIndex = regIt.key();
+ QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> registerNames;
- const QQmlJSScope::ConstPtr seenType = regIt.value().storedType();
- // Don't generate any variables for registers that are initialized with undefined.
- if (seenType.isNull() || seenType == m_typeResolver->voidType())
- continue;
+ auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) {
+ // Don't generate any variables for registers that are initialized with undefined.
+ if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
+ return;
- auto &typesForRegisters = m_registerVariables[registerIndex];
- if (!typesForRegisters.contains(seenType)) {
- QString variableName = u"r%1"_qs.arg(registerIndex);
- if (registerNames.contains(variableName))
- variableName += u'_' + QString::number(typesForRegisters.count());
- registerNames.insert(variableName);
- typesForRegisters[seenType] = variableName;
- }
+ auto &typesForRegisters = m_registerVariables[registerIndex];
+ if (!typesForRegisters.contains(seenType)) {
+ auto &currentRegisterNames = registerNames[registerIndex];
+ QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)];
+ if (name.isEmpty())
+ name = u"r%1_%2"_qs.arg(registerIndex).arg(currentRegisterNames.count());
+ typesForRegisters[seenType] = name;
+ }
+ };
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
+ for (const auto &annotation : *m_annotations) {
+ addVariable(annotation.second.changedRegisterIndex,
+ annotation.second.changedRegister.storedType());
+ for (auto it = annotation.second.typeConversions.begin(),
+ end = annotation.second.typeConversions.end();
+ it != end; ++it) {
+ addVariable(it.key(), it.value().storedType());
}
}
+QT_WARNING_POP
// ensure we have m_labels for loops
for (const auto loopLabel : m_context->labelInfo)
m_labels.insert(loopLabel, u"label_%1"_qs.arg(m_labels.count()));
+ m_state.State::operator=(initialState(function));
const QByteArray byteCode = function->code;
decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
eliminateDeadStores();
@@ -133,11 +154,16 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
QQmlJSAotFunction result;
result.includes.swap(m_includes);
+ QDuplicateTracker<QString> generatedVariables;
for (const auto &registerTypes : qAsConst(m_registerVariables)) {
for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd();
registerTypeIt != end; ++registerTypeIt) {
const QQmlJSScope::ConstPtr storedType = registerTypeIt.key();
+
+ if (generatedVariables.hasSeen(registerTypeIt.value()))
+ continue;
+
result.code += storedType->internalName();
if (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
@@ -153,9 +179,9 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
for (const Section &section : m_sections)
result.code += section.code();
- for (const QQmlJSScope::ConstPtr &argType : qAsConst(function->argumentTypes)) {
- if (argType) {
- result.argumentTypes.append(argType->augmentedInternalName());
+ for (const QQmlJSRegisterContent &argType : qAsConst(function->argumentTypes)) {
+ if (argType.isValid()) {
+ result.argumentTypes.append(argType.storedType()->augmentedInternalName());
} else {
result.argumentTypes.append(u"void"_qs);
}
@@ -184,9 +210,11 @@ QList<QQmlJSCodeGenerator::BasicBlock> QQmlJSCodeGenerator::findBasicBlocks(
const QQmlJSCodeGenerator::Section &section = sections[i];
const QString label = section.label();
if (!label.isEmpty() || currentBlock.jumpMode != JumpMode::None) {
- currentBlock.endSection = i;
- basicBlocks.append(currentBlock);
- currentBlock.beginSection = i;
+ if (currentBlock.beginSection != i) {
+ currentBlock.endSection = i;
+ basicBlocks.append(currentBlock);
+ currentBlock.beginSection = i;
+ }
currentBlock.label = label;
}
currentBlock.jumpMode = section.jumpMode();
@@ -320,6 +348,7 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
usedOnce = true;
}
}
+ requiredRegisters[0][variable] = inUse;
if (!usedOnce) {
registerTypeIt = registerTypes.erase(registerTypeIt);
@@ -332,7 +361,8 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
// Sort the offsets in reverse order so that we can pop from the back
std::sort(toErase.begin(), toErase.end());
- int eraseIndex = toErase.length() - 1;
+ auto end = std::unique(toErase.begin(), toErase.end());
+ int eraseIndex = (end - toErase.begin()) - 1;
for (int i = m_sections.length() - 1; i >= 0 && eraseIndex >= 0; --i) {
if (i != toErase[eraseIndex])
continue;
@@ -361,12 +391,10 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
} while (!toErase.isEmpty() || foundUnknownBlock);
}
-QString QQmlJSCodeGenerator::errorReturnValue() const
+QString QQmlJSCodeGenerator::errorReturnValue()
{
- if (m_function->returnType) {
- return conversion(m_typeResolver->jsPrimitiveType(), m_function->returnType,
- u"QJSPrimitiveValue(QJSPrimitiveUndefined())"_qs);
- }
+ if (m_function->returnType)
+ return conversion(m_typeResolver->voidType(), m_function->returnType, QString());
return QString();
}
@@ -381,29 +409,29 @@ void QQmlJSCodeGenerator::generate_Ret()
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs;
if (!m_state.accumulatorVariableIn.isEmpty()) {
const QString in = use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->varType()) {
+ if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->varType())) {
m_body += u"if (!"_qs + in + u".isValid())\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsPrimitiveType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
m_body += u"if ("_qs + in
+ u".type() == QJSPrimitiveValue::Undefined)\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
m_body += u"if ("_qs + in + u".isUndefined())\n"_qs;
m_body += u" "_qs + signalUndefined;
}
m_body += u"return "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_function->returnType, in);
- } else if (m_function->returnType != m_typeResolver->voidType()
- && m_function->returnType->internalName() != u"void"_qs) {
- if (m_function->returnType->internalName().trimmed().endsWith(u'*')
- || m_function->returnType->internalName().trimmed().endsWith(u'&')) {
- setError(u"Not all paths return a value"_qs);
- return;
+ + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in);
+ } else {
+ if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
+ m_typeResolver->voidType())) {
+ m_body += signalUndefined;
}
-
- m_body += signalUndefined;
- m_body += u"return "_qs + m_function->returnType->internalName() + u"()"_qs;
+ m_body += u"return "_qs + conversion(
+ m_state.accumulatorIn().storedType(), m_function->returnType, QString());
}
} else {
m_body += u"return"_qs;
@@ -426,6 +454,19 @@ static QString toNumericString(double value)
return QString::number(i);
}
+ switch (qFpClassify(value)) {
+ case FP_INFINITE: {
+ const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
+ return std::signbit(value) ? (u'-' + inf) : inf;
+ }
+ case FP_NAN:
+ return u"std::numeric_limits<double>::quiet_NaN()"_qs;
+ case FP_ZERO:
+ return std::signbit(value) ? u"-0.0"_qs : u"0"_qs;
+ default:
+ break;
+ }
+
return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
}
@@ -436,9 +477,14 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index)
auto encodedConst = m_jsUnitGenerator->constant(index);
double value = QV4::StaticValue::fromReturnedValue(encodedConst).doubleValue();
m_body += m_state.accumulatorVariableOut;
- m_body += u" = "_qs;
- m_body += toNumericString(value); // ### handle other types
+
+ // ### handle other types?
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ toNumericString(value));
+
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadZero()
@@ -446,8 +492,10 @@ void QQmlJSCodeGenerator::generate_LoadZero()
INJECT_TRACE_INFO(generate_LoadZero);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = 0"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(), u"0"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadTrue()
@@ -455,8 +503,10 @@ void QQmlJSCodeGenerator::generate_LoadTrue()
INJECT_TRACE_INFO(generate_LoadTrue);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = true"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"true"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadFalse()
@@ -464,43 +514,53 @@ void QQmlJSCodeGenerator::generate_LoadFalse()
INJECT_TRACE_INFO(generate_LoadFalse);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = false"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"false"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadNull()
{
INJECT_TRACE_INFO(generate_LoadNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = nullptr"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut().storedType(),
+ u"nullptr"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->nullType());
}
void QQmlJSCodeGenerator::generate_LoadUndefined()
{
INJECT_TRACE_INFO(generate_LoadUndefined);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
- m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
- u"QJSPrimitiveValue()"_qs) + u";\n"_qs;
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->voidType());
}
void QQmlJSCodeGenerator::generate_LoadInt(int value)
{
INJECT_TRACE_INFO(generate_LoadInt);
- Q_ASSERT(m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->intType()));
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QString::number(value);
+ m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ QString::number(value));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
{
INJECT_TRACE_INFO(generate_MoveConst);
- auto var = registerVariable(destTemp);
+ Q_ASSERT(destTemp == m_state.changedRegisterIndex());
+
+ auto var = changedRegisterVariable();
if (var.isEmpty())
return; // Do not load 'undefined'
@@ -508,48 +568,38 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
const auto v4Value = QV4::StaticValue::fromReturnedValue(
m_jsUnitGenerator->constant(constIndex));
- if (v4Value.isNull()) {
- const auto type = registerType(destTemp).storedType();
- m_body += var + u" = "_qs;
- if (type == m_typeResolver->jsPrimitiveType()) {
- m_body += u"QJSPrimitiveNull()"_qs;
- } else if (type == m_typeResolver->jsValueType()) {
- m_body += u"QJSValue(QJSValue::NullValue)"_qs;
- } else if (type == m_typeResolver->varType()) {
- m_body += u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
- } else if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- m_body += u"nullptr"_qs;
- } else {
- setError(u"Cannot load null into %1"_qs.arg(m_state.accumulatorOut.descriptiveName()));
- }
+ const auto changed = m_state.changedRegister().storedType();
+ QQmlJSScope::ConstPtr contained;
+ QString input;
- m_body += u";\n"_qs;
- return;
+ m_body += var + u" = "_qs;
+ if (v4Value.isNull()) {
+ contained = m_typeResolver->nullType();
+ } else if (v4Value.isUndefined()) {
+ contained = m_typeResolver->voidType();
+ } else if (v4Value.isBoolean()) {
+ contained = m_typeResolver->boolType();
+ input = v4Value.booleanValue() ? u"true"_qs : u"false"_qs;
+ } else if (v4Value.isInteger()) {
+ contained = m_typeResolver->intType();
+ input = QString::number(v4Value.int_32());
+ } else if (v4Value.isDouble()) {
+ contained = m_typeResolver->realType();
+ input = toNumericString(v4Value.doubleValue());
+ } else {
+ reject(u"unknown const type"_qs);
}
-
- double value = 0.0f;
- if (v4Value.isInteger() || v4Value.isBoolean())
- value = v4Value.int_32();
- else if (v4Value.isDouble())
- value = v4Value.doubleValue();
-
- m_body += var;
- m_body += u" = "_qs;
- m_body += toNumericString(value);
- m_body += u";\n"_qs;
+ m_body += conversion(contained, changed, input) + u";\n"_qs;
+ generateOutputVariantConversion(contained);
}
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
{
INJECT_TRACE_INFO(generate_LoadReg);
- // We can't emit any code yet for loading undefined...
- // See also generate_LoadUndefined()
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType()))
- return;
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += use(registerVariable(reg));
+ m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg)));
m_body += u";\n"_qs;
}
@@ -557,14 +607,15 @@ void QQmlJSCodeGenerator::generate_StoreReg(int reg)
{
INJECT_TRACE_INFO(generate_StoreReg);
- Q_ASSERT(m_state.accumulatorIn.isValid());
- const QString var = registerVariable(reg);
+ Q_ASSERT(m_state.changedRegisterIndex() == reg);
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ const QString var = changedRegisterVariable();
m_body.setWriteRegister(var);
if (var.isEmpty())
return; // don't store "undefined"
m_body += var;
m_body += u" = "_qs;
- m_body += conversion(m_state.accumulatorIn, registerType(reg),
+ m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -573,11 +624,15 @@ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
{
INJECT_TRACE_INFO(generate_MoveReg);
- const QString destRegName = registerVariable(destReg);
+ Q_ASSERT(m_state.changedRegisterIndex() == destReg);
+ const QString destRegName = changedRegisterVariable();
m_body.setWriteRegister(destRegName);
+ if (destRegName.isEmpty())
+ return; // don't store things we cannot store.
m_body += destRegName;
m_body += u" = "_qs;
- m_body += use(registerVariable(srcReg));
+ m_body += conversion(registerType(srcReg), m_state.changedRegister(),
+ use(registerVariable(srcReg)));
m_body += u";\n"_qs;
}
@@ -619,8 +674,11 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId));
+ m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(),
+ QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->stringType());
}
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
@@ -646,9 +704,11 @@ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadGlobalLookup);
+ AccumulatorConverter registers(this);
+
const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index)
+ u", &"_qs + m_state.accumulatorVariableOut + u", "_qs
- + metaTypeFromType(m_state.accumulatorOut.storedType()) + u')';
+ + metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
const QString initialization = u"aotContext->initLoadGlobalLookup("_qs
+ QString::number(index) + u')';
generateLookup(lookup, initialization);
@@ -658,55 +718,51 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
- if (m_state.accumulatorVariableOut.isEmpty())
- return;
+ AccumulatorConverter registers(this);
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ conversion(
- m_typeResolver->jsValueType(), m_state.accumulatorOut.storedType(),
+ m_typeResolver->jsValueType(), m_state.accumulatorOut().storedType(),
u"aotContext->javaScriptGlobalProperty("_qs + QString::number(nameIndex) + u")")
+ u";\n"_qs;
return;
}
const QString indexString = QString::number(index);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
const QString lookup = u"aotContext->loadContextIdLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initLoadContextIdLookup("_qs
+ indexString + u')';
generateLookup(lookup, initialization);
return;
}
- const bool isProperty = m_state.accumulatorOut.isProperty();
- const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut.scopeType();
- const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut.storedType();
+ const bool isProperty = m_state.accumulatorOut().isProperty();
+ const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
if (isProperty) {
- m_body += u"{\n"_qs;
-
- const auto lookupType = contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut);
+ const auto lookupType = contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization
= u"aotContext->initLoadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
+ lookupType + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if (m_state.accumulatorOut.isType() || m_state.accumulatorOut.isImportNamespace()) {
+ } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
generateTypeLookup(index);
} else {
- Q_UNREACHABLE();
+ reject(u"lookup of %1"_qs.arg(m_state.accumulatorOut().descriptiveName()));
}
}
@@ -726,10 +782,10 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
switch (type.variant()) {
case QQmlJSRegisterContent::ScopeProperty:
case QQmlJSRegisterContent::ExtensionScopeProperty: {
- if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn)) {
+ if (!m_typeResolver->registerContains(m_state.accumulatorIn(), type.property().type())) {
m_body += u"{\n"_qs;
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, type, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), type, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs + contentPointer(type, u"converted"_qs)
@@ -739,9 +795,9 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
} else {
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs
- + contentPointer(m_state.accumulatorIn, use(m_state.accumulatorVariableIn))
+ + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn))
+ u", "_qs
- + contentType(m_state.accumulatorIn, use(m_state.accumulatorVariableIn)) + u')';
+ + contentType(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u')';
m_body += u";\n"_qs;
}
break;
@@ -767,16 +823,18 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseType = registerType(base);
- if (!m_typeResolver->isNumeric(m_state.accumulatorIn) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) {
reject(u"LoadElement with non-list base type or non-numeric arguments"_qs);
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect LoadElement"_qs);
return;
}
+ AccumulatorConverter registers(this);
+
const QString baseName = use(registerVariable(base));
const QString indexName = use(m_state.accumulatorVariableIn);
@@ -789,7 +847,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
+ u" < "_qs + baseName + u".count(&"_qs + baseName
+ u"))\n"_qs;
m_body += u" "_qs + m_state.accumulatorVariableOut + u" = "_qs +
- conversion(elementType, m_state.accumulatorOut,
+ conversion(elementType, m_state.accumulatorOut(),
baseName + u".at(&"_qs + baseName + u", "_qs
+ indexName + u')') + u";\n"_qs;
m_body += u"else\n"_qs;
@@ -811,7 +869,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect StoreElement"_qs);
return;
}
@@ -828,7 +886,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
+ u"))\n"_qs;
m_body += u" "_qs + baseName + u".replace(&"_qs + baseName
+ u", "_qs + indexName + u", "_qs;
- m_body += conversion(m_state.accumulatorIn, elementType, use(m_state.accumulatorVariableIn))
+ m_body += conversion(m_state.accumulatorIn(), elementType, use(m_state.accumulatorVariableIn))
+ u");\n"_qs;
}
@@ -847,14 +905,14 @@ void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
void QQmlJSCodeGenerator::generateEnumLookup(int index)
{
- const QString enumMember = m_state.accumulatorOut.enumMember();
+ const QString enumMember = m_state.accumulatorOut().enumMember();
// If we're referring to the type, there's nothing to do.
if (enumMember.isEmpty())
return;
// If the metaenum has the value, just use it and skip all the rest.
- const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut.enumeration();
+ const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
if (metaEnum.hasValues()) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ QString::number(metaEnum.value(enumMember));
@@ -862,7 +920,7 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
return;
}
- const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut.scopeType();
+ const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
// Otherwise we would have found an enum with values.
Q_ASSERT(!scopeType->isComposite());
@@ -880,12 +938,13 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
void QQmlJSCodeGenerator::generateTypeLookup(int index)
{
const QString indexString = QString::number(index);
+ const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator);
const QString namespaceString
- = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ = accumulatorIn.isImportNamespace()
+ ? QString::number(accumulatorIn.importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- switch (m_state.accumulatorOut.variant()) {
+ switch (m_state.accumulatorOut().variant()) {
case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
@@ -895,7 +954,6 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
break;
}
case QQmlJSRegisterContent::ScopeModulePrefix:
- m_body += m_state.accumulatorVariableOut + u" = aotContext->qmlScopeObject;\n"_qs;
break;
case QQmlJSRegisterContent::ScopeAttached: {
const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString
@@ -921,33 +979,75 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
}
}
+void QQmlJSCodeGenerator::generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType)
+{
+ if (changedRegisterVariable().isEmpty())
+ return;
+
+ const QQmlJSRegisterContent changed = m_state.changedRegister();
+ if (!m_typeResolver->equals(changed.storedType(), m_typeResolver->varType()))
+ return;
+
+ const QQmlJSScope::ConstPtr target = m_typeResolver->containedType(changed);
+ if (m_typeResolver->equals(target, containedType)
+ || m_typeResolver->equals(target, m_typeResolver->varType())) {
+ return;
+ }
+
+ // If we could store the type directly, we would not wrap it in a QVariant.
+ // Therefore, our best bet here is metaTypeFromName().
+ m_body += changedRegisterVariable() + u".convert("_qs + metaTypeFromName(target) + u");\n"_qs;
+}
+
void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
- if (m_state.accumulatorOut.isMethod()) {
+ if (m_state.accumulatorOut().isMethod()) {
reject(u"lookup of function property."_qs);
return;
}
- if (m_state.accumulatorOut.isEnumeration()) {
- generateEnumLookup(index);
+ if (m_state.accumulatorOut().isImportNamespace()) {
+ Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix);
+ // If we have an object module prefix, we need to pass through the original object.
+ if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs
+ + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
+ use(m_state.accumulatorVariableIn))
+ + u";\n"_qs;
+ } else {
+ m_body.setWriteRegister(QString());
+ }
return;
}
- if (m_state.accumulatorOut.isImportNamespace()) {
- m_body.setWriteRegister(QString());
- return; // Nothing to do. We've resolved the prefix already.
+ AccumulatorConverter registers(this);
+
+ if (m_state.accumulatorOut().isEnumeration()) {
+ generateEnumLookup(index);
+ return;
}
const QString indexString = QString::number(index);
- const QString namespaceString = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
+ ? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- const auto storedType = m_state.accumulatorIn.storedType();
- const bool isReferenceType
- = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectAttached) {
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const bool isReferenceType = (accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Reference);
+
+ switch (m_state.accumulatorOut().variant()) {
+ case QQmlJSRegisterContent::ScopeAttached: {
+ const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString
+ + u", aotContext->qmlScopeObject, &"_qs + m_state.accumulatorVariableOut + u')';
+ const QString initialization = u"aotContext->initLoadAttachedLookup("_qs
+ + indexString + u", "_qs + namespaceString
+ + u", aotContext->qmlScopeObject)"_qs;
+ generateLookup(lookup, initialization);
+ return;
+ }
+ case QQmlJSRegisterContent::ObjectAttached: {
if (!isReferenceType) {
// This can happen on incomplete type information. We contextually know that the
// type must be a QObject, but we cannot construct the inheritance chain. Then we
@@ -964,7 +1064,8 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
+ use(m_state.accumulatorVariableIn) + u')';
generateLookup(lookup, initialization);
return;
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton) {
+ }
+ case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
const QString initialization = u"aotContext->initLoadSingletonLookup("_qs
@@ -972,51 +1073,50 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
generateLookup(lookup, initialization);
return;
}
+ default:
+ break;
+ }
- Q_ASSERT(m_state.accumulatorOut.isProperty());
+ Q_ASSERT(m_state.accumulatorOut().isProperty());
- const QQmlJSScope::ConstPtr out = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr out = m_state.accumulatorOut().storedType();
if (isReferenceType) {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getObjectLookup("_qs + indexString
+ u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initGetObjectLookup("_qs
+ indexString + u", "_qs + use(m_state.accumulatorVariableIn)
- + u", "_qs + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if ((storedType == m_typeResolver->stringType()
- || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType())
+ || accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Sequence)
&& m_jsUnitGenerator->lookupName(index) == u"length"_qs) {
// Special-cased the same way as in QQmlJSTypeResolver::memberType()
m_body += m_state.accumulatorVariableOut + u" = "_qs + use(m_state.accumulatorVariableIn)
+ u".count("_qs;
- if (storedType == m_typeResolver->listPropertyType())
+ if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType()))
m_body += u'&' + use(m_state.accumulatorVariableIn);
m_body += u')' + u";\n"_qs;
- } else if (storedType == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
reject(u"lookup in QJSValue"_qs);
} else {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getValueLookup("_qs + indexString
- + u", "_qs + contentPointer(m_state.accumulatorIn,
+ + u", "_qs + contentPointer(m_state.accumulatorIn(),
use(m_state.accumulatorVariableIn))
- + u", "_qs + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentPointer(m_state.accumulatorOut(),
+ m_state.accumulatorVariableOut)
+ u')';
const QString initialization = u"aotContext->initGetValueLookup("_qs
+ indexString + u", "_qs
- + metaObject(m_state.accumulatorOut.scopeType()) + u", "_qs
- + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + metaObject(m_state.accumulatorOut().scopeType()) + u", "_qs
+ + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
}
}
@@ -1037,10 +1137,10 @@ void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
QString QQmlJSCodeGenerator::setLookupPreparation(
const QQmlJSRegisterContent &content, const QString &arg, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return u"const QMetaType argType = aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u");\n"_qs
+ u"if (argType.isValid())\n "_qs + arg + u".convert(argType)";
@@ -1058,9 +1158,8 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
m_body.setWriteRegister(QString());
const QString indexString = QString::number(index);
- const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
const QQmlJSRegisterContent callBase = registerType(baseReg);
- const QQmlJSScope::ConstPtr objectType = callBase.storedType();
const QQmlJSRegisterContent specific = m_typeResolver->memberType(
callBase, m_jsUnitGenerator->lookupName(index));
const QQmlJSRegisterContent property = specific.storedIn(
@@ -1072,9 +1171,10 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
QString variableInType;
QString preparation;
QString argType;
- if (property != m_state.accumulatorIn) {
+ if (!m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->containedType(property))) {
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, property, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
variableIn = contentPointer(property, u"converted"_qs);
variableInType = contentType(property, u"converted"_qs);
@@ -1089,7 +1189,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
argType = variableInType;
}
- switch (objectType->accessSemantics()) {
+ switch (callBase.storedType()->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference: {
const QString lookup = u"aotContext->setObjectLookup("_qs + indexString
+ u", "_qs + object + u", "_qs + variableIn + u')';
@@ -1105,7 +1205,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
- if (objectType != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) {
reject(u"SetLookup on sequence types (because of missing write-back)"_qs);
break;
}
@@ -1181,12 +1281,14 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
QString args;
QString conversions;
- if (m_typeResolver->containedType(m_state.accumulatorOut) == m_typeResolver->voidType()) {
+ if (m_state.changedRegisterIndex() == InvalidRegister ||
+ m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
types = u"QMetaType()"_qs;
args = u"nullptr"_qs;
} else {
*outVar = u"callResult"_qs;
- const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
m_body += outType->internalName();
if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
m_body += u" *"_qs;
@@ -1194,25 +1296,25 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
m_body += u' ';
m_body += *outVar + u";\n";
- types = metaTypeFromType(m_state.accumulatorOut.storedType());
+ types = metaTypeFromType(m_state.accumulatorOut().storedType());
args = u'&' + *outVar;
}
for (int i = 0; i < argc; ++i) {
- const QQmlJSScope::ConstPtr type = registerType(argv + i).storedType();
+ const QQmlJSRegisterContent content = registerType(argv + i);
const QString var = use(registerVariable(argv + i));
- if (type == m_typeResolver->jsPrimitiveType()) {
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
QString argName = u"arg"_qs + QString::number(i);
conversions += u"QVariant "_qs + argName + u" = "_qs
- + conversion(type, m_typeResolver->varType(), var) + u";\n"_qs;
+ + conversion(content.storedType(), m_typeResolver->varType(), var) + u";\n"_qs;
args += u", "_qs + argName + u".data()"_qs;
types += u", "_qs + argName + u".metaType()"_qs;
- } else if (type == m_typeResolver->varType()) {
+ } else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
args += u", "_qs + var + u".data()"_qs;
types += u", "_qs + var + u".metaType()"_qs;
} else {
args += u", &"_qs + var;
- types += u", "_qs + metaTypeFromType(type);
+ types += u", "_qs + metaTypeFromType(content.storedType());
}
}
return conversions
@@ -1222,17 +1324,13 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
{
- // Generate a new section to set m_state.accumulatorVariableOut,
- // so that m_state.accumulatorVariableOut can be optimized away.
+ if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
+ return;
nextSection();
m_body.setWriteRegister(m_state.accumulatorVariableOut);
m_body += m_state.accumulatorVariableOut + u" = "_qs;
-
- if (outVar.isEmpty())
- m_body += u"{};\n"_qs;
- else
- m_body += u"std::move(" + outVar + u");\n";
+ m_body += u"std::move(" + outVar + u");\n";
nextSection();
m_body.setHasSideEffects(true);
@@ -1272,8 +1370,10 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
addInclude(u"qalgorithms.h"_qs);
addInclude(u"qrandom.h"_qs);
- if (m_state.accumulatorOut.storedType() != m_typeResolver->realType())
- return false;
+ // If the result is not stored, we don't need to generate any code. All the math methods are
+ // conceptually pure functions.
+ if (m_state.changedRegisterIndex() != Accumulator)
+ return true;
m_body += u"{\n"_qs;
for (int i = 0; i < argc; ++i) {
@@ -1287,101 +1387,106 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ QString expression;
+
if (name == u"abs" && argc == 1) {
- m_body += u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
+ expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
} else if (name == u"acos"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
} else if (name == u"acosh"_qs && argc == 1) {
- m_body += u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
} else if (name == u"asin"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
} else if (name == u"asinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
} else if (name == u"atan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
} else if (name == u"atanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs;
} else if (name == u"atan2"_qs) {
// TODO: complicated
return false;
} else if (name == u"cbrt"_qs && argc == 1) {
- m_body += u"std::cbrt(arg1)"_qs;
+ expression = u"std::cbrt(arg1)"_qs;
} else if (name == u"ceil"_qs && argc == 1) {
- m_body += u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs;
+ expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs;
} else if (name == u"clz32"_qs && argc == 1) {
- m_body += u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
+ expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
} else if (name == u"cos"_qs && argc == 1) {
- m_body += u"std::cos(arg1)"_qs;
+ expression = u"std::cos(arg1)"_qs;
} else if (name == u"cosh"_qs && argc == 1) {
- m_body += u"std::cosh(arg1)"_qs;
+ expression = u"std::cosh(arg1)"_qs;
} else if (name == u"exp"_qs && argc == 1) {
- m_body += u"std::isinf(arg1) "
+ expression = u"std::isinf(arg1) "
"? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
": std::exp(arg1)"_qs.arg(inf);
} else if (name == u"expm1"_qs) {
// TODO: complicated
return false;
} else if (name == u"floor"_qs && argc == 1) {
- m_body += u"std::floor(arg1)"_qs;
+ expression = u"std::floor(arg1)"_qs;
} else if (name == u"fround"_qs && argc == 1) {
- m_body += u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
+ expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
"? arg1 "
": double(float(arg1))"_qs;
} else if (name == u"hypot"_qs) {
// TODO: complicated
return false;
} else if (name == u"imul"_qs && argc == 2) {
- m_body += u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
+ expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
"* quint32(QJSNumberCoercion::toInteger(arg2)))"_qs;
} else if (name == u"log"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
} else if (name == u"log10"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
} else if (name == u"log1p"_qs && argc == 1) {
- m_body += u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
} else if (name == u"log2"_qs && argc == 1) {
- m_body += u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
} else if (name == u"max"_qs && argc == 2) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
+ expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
"? arg2 "
": ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs;
} else if (name == u"min"_qs && argc == 2) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
+ expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
"? arg2 "
": ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs;
} else if (name == u"pow"_qs) {
// TODO: complicated
return false;
} else if (name == u"random"_qs && argc == 0) {
- m_body += u"QRandomGenerator::global()->generateDouble()"_qs;
+ expression = u"QRandomGenerator::global()->generateDouble()"_qs;
} else if (name == u"round"_qs && argc == 1) {
- m_body += u"std::isfinite(arg1) "
+ expression = u"std::isfinite(arg1) "
"? ((arg1 < 0.5 && arg1 >= -0.5) "
"? std::copysign(0.0, arg1) "
": std::floor(arg1 + 0.5)) "
": arg1"_qs;
} else if (name == u"sign"_qs && argc == 1) {
- m_body += u"std::isnan(arg1) "
+ expression = u"std::isnan(arg1) "
"? %1 "
": (qIsNull(arg1) "
"? arg1 "
": (std::signbit(arg1) ? -1.0 : 1.0))"_qs.arg(qNaN);
} else if (name == u"sin"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
} else if (name == u"sinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
} else if (name == u"sqrt"_qs && argc == 1) {
- m_body += u"std::sqrt(arg1)"_qs;
+ expression = u"std::sqrt(arg1)"_qs;
} else if (name == u"tan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
} else if (name == u"tanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
} else if (name == u"trunc"_qs && argc == 1) {
- m_body += u"std::trunc(arg1)"_qs;
+ expression = u"std::trunc(arg1)"_qs;
} else {
return false;
}
+ m_body += conversion(
+ m_typeResolver->realType(), m_state.accumulatorOut().storedType(), expression);
+
m_body += u";\n"_qs;
m_body += u"}\n"_qs;
return true;
@@ -1391,11 +1496,16 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
{
INJECT_TRACE_INFO(generate_CallPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
const QQmlJSRegisterContent baseType = registerType(base);
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
const QString name = m_jsUnitGenerator->stringForIndex(
m_jsUnitGenerator->lookupNameIndex(index));
- if (m_typeResolver->containedType(baseType) == mathObject()) {
+ if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) {
if (inlineMathMethod(name, argc, argv))
return;
}
@@ -1458,6 +1568,11 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
{
INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
m_body.setHasSideEffects(true);
const QString indexString = QString::number(index);
@@ -1541,7 +1656,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
generateSetInstructionPointer();
m_body += u"aotContext->engine->throwError("_qs
- + conversion(m_state.accumulatorIn, m_typeResolver->globalType(
+ + conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
m_typeResolver->jsValueType()),
use(m_state.accumulatorVariableIn)) + u");\n"_qs;
m_body += u"return "_qs + errorReturnValue() + u";\n"_qs;
@@ -1674,9 +1789,15 @@ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- reject(u"DefineArray"_qs);
+ if (argc > 0)
+ reject(u"DefineArray"_qs);
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->emptyListType());
}
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1745,7 +1866,7 @@ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
m_body.setWriteRegister(QString());
m_body += u"if ("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1760,7 +1881,7 @@ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
m_body.setWriteRegister(QString());
m_body += u"if (!"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1799,42 +1920,47 @@ void QQmlJSCodeGenerator::generate_CmpEqNull()
{
INJECT_TRACE_INFO(generate_CmlEqNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
-
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeNull()
{
INJECT_TRACE_INFO(generate_CmlNeNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
{
- if (m_state.accumulatorIn.storedType() == m_typeResolver->intType())
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType()))
return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->boolType()) {
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) {
return QString::number(lhsConst) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->intType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
use(m_state.accumulatorVariableIn));
}
- if (m_typeResolver->isNumeric(m_state.accumulatorIn)) {
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
return conversion(m_typeResolver->intType(), m_typeResolver->realType(),
QString::number(lhsConst)) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->realType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(),
use(m_state.accumulatorVariableIn));
}
@@ -1842,7 +1968,7 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(),
QString::number(lhsConst));
result += u".equals("_qs;
- result += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
result += u')';
return result;
@@ -1851,10 +1977,10 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
QString QQmlJSCodeGenerator::getLookupPreparation(
const QQmlJSRegisterContent &content, const QString &var, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return var + u" = QVariant(aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u"))"_qs;
}
@@ -1865,9 +1991,9 @@ QString QQmlJSCodeGenerator::getLookupPreparation(
QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored)
+ if (m_typeResolver->registerContains(content, stored))
return u'&' + var;
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType()))
return var + u".data()"_qs;
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u'&' + var;
@@ -1881,9 +2007,9 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
const QQmlJSScope::ConstPtr stored = content.storedType();
const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
m_typeResolver->containedType(content));
- if (contained == stored)
+ if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->equals(stored, m_typeResolver->varType()))
return var + u".metaType()"_qs; // We expect the QVariant to be initialized
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return metaTypeFromName(contained);
@@ -1896,16 +2022,20 @@ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpEqInt);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + eqIntExpression(lhsConst)
- + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ eqIntExpression(lhsConst)) + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpNeInt);
- m_body += m_state.accumulatorVariableOut + u" = !("_qs + eqIntExpression(lhsConst)
- + u");\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!("_qs + eqIntExpression(lhsConst) + u')') + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
@@ -1973,17 +2103,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
INJECT_TRACE_INFO(generate_As);
const QString input = use(registerVariable(lhs));
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut);
+ const QQmlJSScope::ConstPtr contained
+ = m_typeResolver->containedType(m_state.readRegister(lhs));
m_body += m_state.accumulatorVariableOut + u" = "_qs;
- if (m_state.accumulatorIn.storedType() == m_typeResolver->metaObjectType()
+ if (m_typeResolver->equals(
+ m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())
&& contained->isComposite()) {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
use(m_state.accumulatorVariableIn) + u"->cast("_qs + input + u')');
} else {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
u'(' + metaObject(contained) + u")->cast("_qs + input + u')');
}
m_body += u";\n"_qs;
@@ -1992,31 +2124,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
void QQmlJSCodeGenerator::generate_UNot()
{
INJECT_TRACE_INFO(generate_UNot);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"!"_qs);
}
void QQmlJSCodeGenerator::generate_UPlus()
{
INJECT_TRACE_INFO(generate_UPlus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= +"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"+"_qs);
}
void QQmlJSCodeGenerator::generate_UMinus()
{
INJECT_TRACE_INFO(generate_UMinus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= -"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"-"_qs);
}
void QQmlJSCodeGenerator::generate_UCompl()
@@ -2027,25 +2147,13 @@ void QQmlJSCodeGenerator::generate_UCompl()
void QQmlJSCodeGenerator::generate_Increment()
{
INJECT_TRACE_INFO(generate_Increment);
- if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
- m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn)) + u"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"++"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"++"_qs);
}
void QQmlJSCodeGenerator::generate_Decrement()
{
INJECT_TRACE_INFO(generate_Decrement);
- if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
- m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn)) + u"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"--"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"--"_qs);
}
void QQmlJSCodeGenerator::generate_Add(int lhs)
@@ -2152,16 +2260,18 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
use(registerVariable(lhs)));
const auto rhsVar = conversion(
- m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
+ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
u'(' + lhsVar + u" % "_qs + rhsVar + u')');
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->jsPrimitiveType());
}
void QQmlJSCodeGenerator::generate_Sub(int lhs)
@@ -2217,9 +2327,15 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
- m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
- .value(m_state.accumulatorIn.storedType());
- Q_ASSERT(!m_state.accumulatorIn.isValid() || !m_state.accumulatorVariableIn.isEmpty());
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end()
+ && isTypeStorable(m_typeResolver, accumulatorIn.value().storedType())) {
+ m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
+ .value(accumulatorIn.value().storedType());
+ Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
+ } else {
+ m_state.accumulatorVariableIn.clear();
+ }
auto labelIt = m_labels.constFind(currentInstructionOffset());
if (labelIt != m_labels.constEnd()) {
@@ -2234,38 +2350,38 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
nextSection();
- m_state.accumulatorVariableOut = registerVariable(QQmlJSTypePropagator::Accumulator);
+ if (m_state.changedRegisterIndex() == Accumulator)
+ m_state.accumulatorVariableOut = changedRegisterVariable();
+ else
+ m_state.accumulatorVariableOut.clear();
+
if (!m_state.accumulatorVariableOut.isEmpty())
m_body.setWriteRegister(m_state.accumulatorVariableOut);
// If the accumulator type is valid, we want an accumulator variable.
// If not, we don't want one.
- // If the stored type is void, we don't need a variable, but we want to transport the type
- // information for any enum access or similar.
- Q_ASSERT((m_state.accumulatorOut.isValid()
- && m_state.accumulatorOut.storedType() != m_typeResolver->voidType())
+ Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
|| m_state.accumulatorVariableOut.isEmpty());
- Q_ASSERT(!m_state.accumulatorOut.isValid()
- || m_state.accumulatorOut.storedType() == m_typeResolver->voidType()
- || !m_state.accumulatorVariableOut.isEmpty());
+ Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
+ || !m_state.accumulatorVariableOut.isEmpty()
+ || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
const int currentLine = currentSourceLocation().startLine;
if (currentLine != m_lastLineNumberUsed) {
const int nextLine = nextJSLine(currentLine);
- if (nextLine == currentLine + 1 || nextLine < 1) {
+ for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
m_body += u"// "_qs;
- m_body += m_sourceCodeLines.value(currentLine - 1).trimmed();
+ m_body += m_sourceCodeLines.value(line).trimmed();
m_body += u'\n';
- } else {
- m_body += u"/*\n"_qs;
- for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
- m_body += m_sourceCodeLines.at(line).trimmed();
- m_body += u'\n';
- }
- m_body += u"*/\n"_qs;
}
m_lastLineNumberUsed = currentLine;
}
+
+ // If the instruction has no side effects and doesn't write any register, it's dead.
+ // We might still need the label, though, and the source code comment.
+ if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty())
+ return SkipInstruction;
+
return ProcessInstruction;
}
@@ -2292,79 +2408,134 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
const QQmlJSRegisterContent lhsContent = registerType(lhs);
auto isComparable = [&]() {
if (m_typeResolver->isPrimitive(lhsContent)
- && m_typeResolver->isPrimitive(m_state.accumulatorIn)) {
+ && m_typeResolver->isPrimitive(m_state.accumulatorIn())) {
return true;
}
- if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn.isEnumeration())
+ if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration())
return true;
- if (m_typeResolver->isNumeric(m_state.accumulatorIn) && lhsContent.isEnumeration())
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration())
return true;
return false;
};
- if (!isComparable())
- reject(u"equality comparison on non-primitive types"_qs);
+ if (!isComparable()) {
+ reject(u"equality comparison on non-primitive types %1 and %2"_qs.arg(
+ m_state.accumulatorIn().descriptiveName(), lhsContent.descriptiveName()));
+ }
const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
- const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType();
m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto primitive = m_typeResolver->jsPrimitiveType();
- if (lhsType == rhsType && lhsType != primitive) {
- m_body += use(registerVariable(lhs));
- m_body += (invert ? u" != "_qs : u" == "_qs);
- m_body += use(m_state.accumulatorVariableIn);
+ if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ use(registerVariable(lhs)) + (invert ? u" != "_qs : u" == "_qs)
+ + use(m_state.accumulatorVariableIn));
} else {
- if (invert)
- m_body += u'!';
- m_body += conversion(registerType(lhs).storedType(), primitive, use(registerVariable(lhs)));
- m_body += u'.';
- m_body += function;
- m_body += u'(';
- m_body += conversion(m_state.accumulatorIn.storedType(), primitive,
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ (invert ? u"!"_qs : QString())
+ + conversion(registerType(lhs).storedType(), primitive,
+ use(registerVariable(lhs)))
+ + u'.' + function + u'(' + conversion(
+ m_state.accumulatorIn().storedType(), primitive,
+ use(m_state.accumulatorVariableIn))
+ + u')');
}
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
{
- m_body += m_state.accumulatorVariableOut;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto lhsType = registerType(lhs);
const QQmlJSScope::ConstPtr compareType =
- m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn)
- ? m_typeResolver->merge(lhsType, m_state.accumulatorIn).storedType()
+ m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
+ ? m_typeResolver->merge(lhsType, m_state.accumulatorIn()).storedType()
: m_typeResolver->jsPrimitiveType();
- m_body += u" = "_qs;
- m_body += conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)));
- m_body += u' ';
- m_body += cppOperator;
- m_body += u' ';
- m_body += conversion(m_state.accumulatorIn.storedType(), compareType,
- use(m_state.accumulatorVariableIn));
+
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)))
+ + u' ' + cppOperator + u' '
+ + conversion(m_state.accumulatorIn().storedType(), compareType,
+ use(m_state.accumulatorVariableIn)));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
{
- const auto lhsVar = conversion(registerType(lhs), m_state.accumulatorOut,
+ const auto lhsVar = conversion(registerType(lhs), m_state.readRegister(lhs),
use(registerVariable(lhs)));
- const auto rhsVar = conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
+ const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += lhsVar;
- m_body += u' ';
- m_body += cppOperator;
- m_body += u' ';
- m_body += rhsVar;
+ m_body += conversion(
+ originalOut, m_state.accumulatorOut(),
+ u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
+}
+
+void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
+{
+ const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
+ use(m_state.accumulatorVariableIn));
+
+ if (var == m_state.accumulatorVariableOut) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + cppOperator + var + u";\n"_qs;
+ return;
+ }
+
+ const auto original = m_typeResolver->original(m_state.accumulatorOut());
+ if (m_state.accumulatorOut() == original) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs
+ + cppOperator + m_state.accumulatorVariableOut + u";\n"_qs;
+ return;
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
+ m_typeResolver->original(m_state.accumulatorOut()),
+ m_state.accumulatorOut(), cppOperator + var) + u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
+}
+
+void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
+{
+ const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
+ use(m_state.accumulatorVariableIn));
+
+ if (var == m_state.accumulatorVariableOut) {
+ m_body += cppOperator + var + u";\n"_qs;
+ return;
+ }
+
+ const auto original = m_typeResolver->original(m_state.accumulatorOut());
+ if (m_state.accumulatorOut() == original) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs;
+ m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_qs;
+ return;
+ }
+
+ m_body += u"{\n"_qs;
+ m_body += u"auto converted = "_qs + var + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
+ m_typeResolver->original(m_state.accumulatorOut()),
+ m_state.accumulatorOut(), cppOperator + u"converted"_qs) + u";\n"_qs;
+ m_body += u"}\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
@@ -2381,19 +2552,6 @@ void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &i
m_body += u"}\n"_qs;
}
-void QQmlJSCodeGenerator::protectAccumulator()
-{
- // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
- // prepare the output QVariant, and afterwards use the input variant. Therefore we need to move
- // the input out of the way first.
- if (m_state.accumulatorVariableIn == m_state.accumulatorVariableOut
- && m_state.accumulatorOut.storedType() == m_typeResolver->varType()) {
- m_state.accumulatorVariableIn = use(m_state.accumulatorVariableIn) + u"_moved"_qs;
- m_body += u"QVariant "_qs + m_state.accumulatorVariableIn
- + u" = std::move("_qs + m_state.accumulatorVariableOut + u");\n"_qs;
- }
-}
-
void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset, JumpMode mode)
{
@@ -2402,32 +2560,47 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
m_body += u"{\n"_qs;
int absoluteOffset =nextInstructionOffset() + relativeOffset;
- const auto annotation = m_annotations->constFind(absoluteOffset);
+ const auto annotation = m_annotations->find(absoluteOffset);
if (annotation != m_annotations->constEnd()) {
- const auto &currentTypes = (*m_annotations)[currentInstructionOffset()].registers;
- const auto &conversions = annotation->expectedTargetTypesBeforeJump;
+ const auto &conversions = annotation->second.typeConversions;
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
regIt != regEnd; ++regIt) {
int registerIndex = regIt.key();
const QQmlJSRegisterContent targetType = regIt.value();
- if (!targetType.isValid() || !currentTypes.contains(registerIndex))
+ if (!targetType.isValid())
continue;
- const QQmlJSRegisterContent currentType = currentTypes.value(registerIndex);
- if (!currentType.isValid() || currentType == targetType)
+
+ QQmlJSRegisterContent currentType;
+ QString currentVariable;
+ if (registerIndex == m_state.changedRegisterIndex()) {
+ currentType = m_state.changedRegister();
+ currentVariable = changedRegisterVariable();
+ } else {
+ auto it = m_state.registers.find(registerIndex);
+ if (it == m_state.registers.end())
+ continue;
+ currentType = it.value();
+ currentVariable = registerVariable(registerIndex);
+ }
+
+ // Actually == here. We want the jump code also for equal types
+ if (currentType == targetType
+ || !isTypeStorable(m_typeResolver, targetType.storedType())) {
continue;
+ }
+
Q_ASSERT(m_registerVariables.contains(registerIndex));
const auto &currentRegisterVariables = m_registerVariables[registerIndex];
const auto variable = currentRegisterVariables.constFind(targetType.storedType());
- const QString oldVar = registerVariable(registerIndex);
- if (variable == currentRegisterVariables.end() || *variable == oldVar)
+ if (variable == currentRegisterVariables.end() || *variable == currentVariable)
continue;
nextSection();
m_body.setWriteRegister(*variable);
m_body += *variable;
m_body += u" = "_qs;
- m_body += conversion(currentTypes.value(registerIndex), targetType, use(oldVar));
+ m_body += conversion(currentType, targetType, use(currentVariable));
m_body += u";\n"_qs;
}
}
@@ -2449,117 +2622,210 @@ QString QQmlJSCodeGenerator::registerVariable(int index) const
if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) {
const int argumentIndex = index - QV4::CallData::OffsetCount;
return u"*static_cast<"_qs
- + castTargetName(m_function->argumentTypes[argumentIndex])
+ + castTargetName(m_function->argumentTypes[argumentIndex].storedType())
+ u"*>(argumentsPtr["_qs + QString::number(argumentIndex) + u"])"_qs;
}
return m_registerVariables.value(index).value(registerType(index).storedType());
}
+QString QQmlJSCodeGenerator::changedRegisterVariable() const
+{
+ return m_registerVariables.value(m_state.changedRegisterIndex()).value(
+ m_state.changedRegister().storedType());
+}
+
QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
{
- if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) {
- return m_typeResolver->globalType(
- m_function->argumentTypes[index - QV4::CallData::OffsetCount]);
- }
+ if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex())
+ return m_function->argumentTypes[index - QV4::CallData::OffsetCount];
return m_state.registers[index];
}
QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
- const QString &variable) const
+ const QString &variable)
{
// TODO: most values can be moved, which is much more efficient with the common types.
// add a move(from, to, variable) function that implements the moves.
Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
- if (from == to)
+ const auto jsValueType = m_typeResolver->jsValueType();
+ const auto varType = m_typeResolver->varType();
+ const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
+ const auto boolType = m_typeResolver->boolType();
+
+ auto zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) {
+ if (m_typeResolver->equals(to, boolType))
+ return u"false"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
+ return u"0"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->floatType()))
+ return u"0.0f"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
+ return u"0.0"_qs;
+ return QString();
+ };
+
+ if (m_typeResolver->equals(from, m_typeResolver->voidType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
+ const QString zero = zeroBoolOrNumeric(to);
+ if (!zero.isEmpty())
+ return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
+ return QQmlJSUtils::toLiteral(u"undefined"_qs);
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ // Anything else is just the default constructed type.
+ return to->augmentedInternalName() + u"()"_qs;
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->nullType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
+ if (m_typeResolver->equals(to, jsValueType))
+ return u"QJSValue(QJSValue::NullValue)"_qs;
+ if (m_typeResolver->equals(to, jsPrimitiveType))
+ return u"QJSPrimitiveValue(QJSPrimitiveNull())"_qs;
+ if (m_typeResolver->equals(to, varType))
+ return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
+ const QString zero = zeroBoolOrNumeric(to);
+ if (!zero.isEmpty())
+ return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
+ return QQmlJSUtils::toLiteral(u"null"_qs);
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ reject(u"Conversion from null to %1"_qs.arg(to->internalName()));
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->emptyListType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ return castTargetName(to) + u"()"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->varType()))
+ return u"QVariant(QVariantList())"_qs;
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ reject(u"Conversion from empty list to %1"_qs.arg(to->internalName()));
+ }
+
+ if (m_typeResolver->equals(from, to))
return variable;
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
// We still have to cast as other execution paths may result in different types.
- if (base == to)
+ if (m_typeResolver->equals(base, to))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
- if (base == from)
+ if (m_typeResolver->equals(base, from))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
- } else if (to == m_typeResolver->boolType()) {
+ } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
return u'(' + variable + u" != nullptr)"_qs;
}
}
- if (from == m_typeResolver->nullType()
- && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
- }
-
- const auto jsValueType = m_typeResolver->jsValueType();
-
auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
- return candidate == jsValueType || candidate->isScript();
+ return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
};
if (isJsValue(from) && isJsValue(to))
return variable;
- const auto boolType = m_typeResolver->boolType();
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
- || type == m_typeResolver->boolType()
+ || m_typeResolver->equals(type, m_typeResolver->boolType())
|| type->scopeType() == QQmlJSScope::EnumScope;
};
- if (from == m_typeResolver->realType() && to == m_typeResolver->intType())
+ if (m_typeResolver->equals(from, m_typeResolver->realType())
+ && m_typeResolver->equals(to, m_typeResolver->intType())) {
return u"QJSNumberCoercion::toInteger("_qs + variable + u')';
+ }
if (isBoolOrNumber(from) && isBoolOrNumber(to))
return to->internalName() + u'(' + variable + u')';
- const auto varType = m_typeResolver->varType();
- const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
- if (from == jsPrimitiveType) {
- if (to == m_typeResolver->realType())
+
+ if (m_typeResolver->equals(from, jsPrimitiveType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
return variable + u".toDouble()"_qs;
- if (to == boolType)
+ if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_qs;
- if (to == m_typeResolver->intType())
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
return variable + u".toInteger()"_qs;
- if (to == m_typeResolver->stringType())
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return variable + u".toString()"_qs;
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"QJSValue(QJSPrimitiveValue("_qs + variable + u"))"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant()"_qs;
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
}
if (isJsValue(from)) {
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return variable + u".toPrimitive()"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant(QJSValue::RetainJSObjects)"_qs;
return u"qjsvalue_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
}
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return u"QJSPrimitiveValue("_qs + variable + u')';
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"aotContext->engine->toScriptValue("_qs + variable + u')';
- if (from == varType) {
- if (to == m_typeResolver->listPropertyType())
+ if (m_typeResolver->equals(from, varType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->listPropertyType()))
return u"QQmlListReference("_qs + variable + u", aotContext->qmlEngine())"_qs;
- return u"qvariant_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
+ return u"aotContext->engine->fromVariant<"_qs + castTargetName(to) + u">("_qs
+ + variable + u')';
}
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return u"QVariant::fromValue("_qs + variable + u')';
+ if (m_typeResolver->equals(from, m_typeResolver->urlType())
+ && m_typeResolver->equals(to, m_typeResolver->stringType())) {
+ return variable + u".toString()"_qs;
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->stringType())
+ && m_typeResolver->equals(to, m_typeResolver->urlType())) {
+ return u"QUrl("_qs + variable + u')';
+ }
+
+ const auto retrieveFromPrimitive = [&](const QQmlJSScope::ConstPtr &type)
+ {
+ if (m_typeResolver->equals(type, m_typeResolver->boolType()))
+ return u".toBool()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->intType()))
+ return u".toInteger()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->realType()))
+ return u".toDouble()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->stringType()))
+ return u".toString()"_qs;
+ return QString();
+ };
+
+ const auto fitsIntoPrimitive = [&](const QQmlJSScope::ConstPtr &type)
+ {
+ return !retrieveFromPrimitive(type).isEmpty()
+ || m_typeResolver->equals(type, m_typeResolver->floatType());
+ };
+
+ if (fitsIntoPrimitive(from)) {
+ const QString retrieve = retrieveFromPrimitive(to);
+ if (!retrieve.isEmpty())
+ return u"QJSPrimitiveValue("_qs + variable + u')' + retrieve;
+ }
+
// TODO: more efficient string conversions, possibly others
return u"aotContext->engine->fromScriptValue<"_qs + castTargetName(to)
@@ -2586,4 +2852,58 @@ void QQmlJSCodeGenerator::reject(const QString &thing)
setError(u"Cannot generate efficient code for %1"_qs.arg(thing));
}
+QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
+ : accumulatorOut(generator->m_state.accumulatorOut())
+ , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
+ , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
+ , generator(generator)
+{
+ if (accumulatorVariableOut.isEmpty())
+ return;
+
+ const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
+ const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
+ const QQmlJSScope::ConstPtr origStored = resolver->originalType(accumulatorOut.storedType());
+
+ if (!resolver->equals(origContained, resolver->containedType(accumulatorOut))
+ || !resolver->equals(origStored, accumulatorOut.storedType())) {
+ generator->m_state.accumulatorVariableOut = u"retrieved"_qs;
+ generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut));
+ generator->m_body += u"{\n"_qs;
+ generator->m_body += origStored->augmentedInternalName() + u' '
+ + generator->m_state.accumulatorVariableOut + u";\n";
+ } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
+ && generator->m_state.readsRegister(Accumulator)
+ && resolver->registerIsStoredIn(
+ generator->m_state.accumulatorOut(), resolver->varType())) {
+ // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
+ // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
+ // move the input out of the way first.
+ generator->m_state.accumulatorVariableIn = generator->use(
+ generator->m_state.accumulatorVariableIn) + u"_moved"_qs;
+ generator->m_body += u"{\n"_qs;
+ generator->m_body += u"QVariant "_qs + generator->m_state.accumulatorVariableIn
+ + u" = std::move("_qs + generator->m_state.accumulatorVariableOut + u");\n"_qs;
+ }
+}
+
+QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
+{
+ if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
+ generator->m_body += accumulatorVariableOut + u" = "_qs + generator->conversion(
+ generator->m_state.accumulatorOut(), accumulatorOut,
+ generator->m_state.accumulatorVariableOut) + u";\n"_qs;
+ const auto contained = generator->m_typeResolver->containedType(
+ generator->m_state.accumulatorOut());
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.setRegister(Accumulator, accumulatorOut);
+ generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
+ generator->generateOutputVariantConversion(contained);
+ } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
+ }
+}
+
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 7ef865f551..5128b585d7 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -117,6 +117,22 @@ protected:
QString accumulatorVariableOut;
};
+ // This is an RAII helper we can use to automatically convert the result of "inflexible"
+ // operations to the desired type. For example GetLookup can only retrieve the type of
+ // the property we're looking up. If we want to store a different type, we need to convert.
+ struct AccumulatorConverter
+ {
+ Q_DISABLE_COPY_MOVE(AccumulatorConverter);
+ AccumulatorConverter(QQmlJSCodeGenerator *generator);
+ ~AccumulatorConverter();
+
+ private:
+ const QQmlJSRegisterContent accumulatorOut;
+ const QString accumulatorVariableIn;
+ const QString accumulatorVariableOut;
+ QQmlJSCodeGenerator *generator = nullptr;
+ };
+
virtual QString metaObject(const QQmlJSScope::ConstPtr &objectType);
void generate_Ret() override;
@@ -272,16 +288,16 @@ protected:
QString conversion(const QQmlJSRegisterContent &from,
const QQmlJSRegisterContent &to,
- const QString &variable) const
+ const QString &variable)
{
return conversion(from.storedType(), to.storedType(), variable);
}
QString conversion(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
- const QString &variable) const;
+ const QString &variable);
- QString errorReturnValue() const;
+ QString errorReturnValue();
void reject(const QString &thing);
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const;
@@ -299,6 +315,7 @@ protected:
void generateEnumLookup(int index);
QString registerVariable(int index) const;
+ QString changedRegisterVariable() const;
QQmlJSRegisterContent registerType(int index) const;
Section m_body;
@@ -326,15 +343,16 @@ private:
void generateCompareOperation(int lhs, const QString &cppOperator);
void generateArithmeticOperation(int lhs, const QString &cppOperator);
void generateJumpCodeWithTypeConversions(int relativeOffset, JumpMode mode);
+ void generateUnaryOperation(const QString &cppOperator);
+ void generateInPlaceOperation(const QString &cppOperator);
void generateMoveOutVar(const QString &outVar);
void generateTypeLookup(int index);
+ void generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType);
QString eqIntExpression(int lhsConst);
QString argumentsList(int argc, int argv, QString *outVar);
QString castTargetName(const QQmlJSScope::ConstPtr &type) const;
- void protectAccumulator();
-
QList<BasicBlock> findBasicBlocks(const QList<Section> &sections);
RequiredRegisters dropPreserveCycles(
const QList<BasicBlock> &basicBlocks, const RequiredRegisters &requiredRegisters);
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index d7f9336ee9..af5c9204e6 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -46,6 +46,7 @@
#include <private/qqmljstyperesolver_p.h>
#include <private/qv4bytecodehandler_p.h>
#include <private/qv4compiler_p.h>
+#include <private/qflatmap_p.h>
QT_BEGIN_NAMESPACE
@@ -54,6 +55,7 @@ class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
public:
enum RegisterShortcuts {
+ InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
FirstArgument = QV4::CallData::OffsetCount
};
@@ -61,20 +63,28 @@ public:
using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable;
// map from register index to expected type
- using VirtualRegisters = QHash<int, QQmlJSRegisterContent>;
+ using VirtualRegisters = QFlatMap<int, QQmlJSRegisterContent>;
struct InstructionAnnotation
{
- VirtualRegisters registers;
- VirtualRegisters expectedTargetTypesBeforeJump;
+ // Registers explicit read as part of the instruction.
+ VirtualRegisters readRegisters;
+
+ // Registers that have to be converted for future instructions after a jump.
+ VirtualRegisters typeConversions;
+
+ QQmlJSRegisterContent changedRegister;
+ int changedRegisterIndex = InvalidRegister;
+ bool hasSideEffects = false;
+ bool isRename = false;
};
- using InstructionAnnotations = QHash<int, InstructionAnnotation>;
+ using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
struct Function
{
QQmlJSScopesById addressableScopes;
- QList<QQmlJSScope::ConstPtr> argumentTypes;
+ QList<QQmlJSRegisterContent> argumentTypes;
QQmlJSScope::ConstPtr returnType;
QQmlJSScope::ConstPtr qmlScope;
QByteArray code;
@@ -86,8 +96,80 @@ public:
struct State
{
VirtualRegisters registers;
- QQmlJSRegisterContent accumulatorIn;
- QQmlJSRegisterContent accumulatorOut;
+
+ const QQmlJSRegisterContent &accumulatorIn() const
+ {
+ auto it = registers.find(Accumulator);
+ Q_ASSERT(it != registers.end());
+ return it.value();
+ };
+
+ const QQmlJSRegisterContent &accumulatorOut() const
+ {
+ Q_ASSERT(m_changedRegisterIndex == Accumulator);
+ return m_changedRegister;
+ };
+
+ void setRegister(int registerIndex, QQmlJSRegisterContent content)
+ {
+ m_changedRegister = std::move(content);
+ m_changedRegisterIndex = registerIndex;
+ }
+
+ void clearChangedRegister()
+ {
+ m_changedRegisterIndex = InvalidRegister;
+ m_changedRegister = QQmlJSRegisterContent();
+ }
+
+ int changedRegisterIndex() const { return m_changedRegisterIndex; }
+ const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; }
+
+ void addReadRegister(int registerIndex, const QQmlJSRegisterContent &reg)
+ {
+ Q_ASSERT(reg.isConversion());
+ m_readRegisters[registerIndex] = reg;
+ }
+
+ void addReadAccumulator(const QQmlJSRegisterContent &reg)
+ {
+ addReadRegister(Accumulator, reg);
+ }
+
+ VirtualRegisters takeReadRegisters() const { return std::move(m_readRegisters); }
+ void setReadRegisters(VirtualRegisters readReagisters)
+ {
+ m_readRegisters = std::move(readReagisters);
+ }
+
+ QQmlJSRegisterContent readRegister(int registerIndex) const
+ {
+ Q_ASSERT(m_readRegisters.contains(registerIndex));
+ return m_readRegisters[registerIndex];
+ }
+
+ QQmlJSRegisterContent readAccumulator() const
+ {
+ return readRegister(Accumulator);
+ }
+
+ bool readsRegister(int registerIndex) const
+ {
+ return m_readRegisters.contains(registerIndex);
+ }
+
+ bool hasSideEffects() const { return m_hasSideEffects; }
+ void setHasSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; }
+
+ bool isRename() const { return m_isRename; }
+ void setIsRename(bool isRename) { m_isRename = isRename; }
+
+ private:
+ VirtualRegisters m_readRegisters;
+ QQmlJSRegisterContent m_changedRegister;
+ int m_changedRegisterIndex = InvalidRegister;
+ bool m_hasSideEffects = false;
+ bool m_isRename = false;
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
@@ -105,12 +187,12 @@ protected:
const Function *m_function = nullptr;
QQmlJS::DiagnosticMessage *m_error = nullptr;
- State initialState(const Function *function, const QQmlJSTypeResolver *resolver)
+ State initialState(const Function *function)
{
State state;
for (int i = 0; i < function->argumentTypes.count(); ++i) {
- state.registers[FirstArgument + i]
- = resolver->globalType(function->argumentTypes.at(i));
+ state.registers[FirstArgument + i] = function->argumentTypes.at(i);
+ Q_ASSERT(state.registers[FirstArgument + i].isValid());
}
return state;
}
@@ -120,23 +202,29 @@ protected:
{
State newState;
+ const auto instruction = annotations.find(currentInstructionOffset());
+ newState.registers = oldState.registers;
+
// Usually the initial accumulator type is the output of the previous instruction, but ...
- newState.accumulatorIn = oldState.accumulatorOut;
-
- const auto instruction = annotations.constFind(currentInstructionOffset());
- if (instruction != annotations.constEnd()) {
- const auto target = instruction->expectedTargetTypesBeforeJump.constFind(Accumulator);
- if (target != instruction->expectedTargetTypesBeforeJump.constEnd()) {
- // ... the initial type of the accumulator is given in expectedTargetTypesBeforeJump
- // if the current instruction can be jumped to.
- newState.accumulatorIn = *target;
- }
-
- newState.registers = instruction->registers;
- newState.accumulatorOut = instruction->registers[Accumulator];
- } else {
- newState.registers = VirtualRegisters();
- newState.accumulatorOut = QQmlJSRegisterContent();
+ if (oldState.changedRegisterIndex() != InvalidRegister)
+ newState.registers[oldState.changedRegisterIndex()] = oldState.changedRegister();
+
+ if (instruction == annotations.constEnd())
+ return newState;
+
+ newState.setHasSideEffects(instruction->second.hasSideEffects);
+ newState.setReadRegisters(instruction->second.readRegisters);
+ newState.setIsRename(instruction->second.isRename);
+
+ for (auto it = instruction->second.typeConversions.begin(),
+ end = instruction->second.typeConversions.end(); it != end; ++it) {
+ Q_ASSERT(it.key() != InvalidRegister);
+ newState.registers[it.key()] = it.value();
+ }
+
+ if (instruction->second.changedRegisterIndex != InvalidRegister) {
+ newState.setRegister(instruction->second.changedRegisterIndex,
+ instruction->second.changedRegister);
}
return newState;
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index f9448f38ac..c765a1c94e 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -29,6 +29,7 @@
#include "qqmljscompiler_p.h"
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmljsbasicblocks_p.h>
#include <private/qqmljscodegenerator_p.h>
#include <private/qqmljsfunctioninitializer_p.h>
#include <private/qqmljsimportvisitor_p.h>
@@ -669,26 +670,17 @@ QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const
{
if (isStrict(m_document)
- && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
- && m_logger->isCategoryError(Log_Compiler)) {
+ && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
+ && !m_logger->isCategoryIgnored(Log_Compiler)
+ && m_logger->categoryLevel(Log_Compiler) == QtCriticalMsg) {
qFatal("%s:%d: (strict mode) %s",
qPrintable(QFileInfo(m_resourcePath).fileName()),
location.startLine, qPrintable(message));
}
- switch (type) {
- case QtDebugMsg:
- case QtInfoMsg:
- m_logger->logInfo(message, Log_Compiler, location);
- break;
- case QtWarningMsg:
- m_logger->logWarning(message, Log_Compiler, location);
- break;
- case QtCriticalMsg:
- case QtFatalMsg:
- m_logger->logCritical(message, Log_Compiler, location);
- break;
- }
+ // TODO: this is a special place that explicitly sets the severity through
+ // logger's private function
+ m_logger->log(message, Log_Compiler, location, type);
return QQmlJS::DiagnosticMessage {
message,
@@ -777,6 +769,9 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
if (error->isValid())
return compileError();
+ QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
+ typePropagationResult = basicBlocks.run(function, typePropagationResult);
+
QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
shadowCheck.run(&typePropagationResult, function, error);
if (error->isValid())
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 8cd3bb3536..c993197399 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -92,14 +92,20 @@ void QQmlJSFunctionInitializer::populateSignature(
for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) {
if (argument.typeAnnotation) {
if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) {
- function->argumentTypes.append(type);
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(type)));
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(m_typeResolver->varType())));
signatureError(u"Cannot resolve the argument type %1."_qs
.arg(argument.typeAnnotation->type->toString()));
}
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(m_typeResolver->varType())));
signatureError(u"Functions without type annotations won't be compiled"_qs);
}
}
@@ -181,7 +187,11 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
}
const auto property = m_objectType->property(propertyName);
- function.returnType = property.type();
+ function.returnType = property.isList()
+ ? m_typeResolver->listType(property.type())
+ : QQmlJSScope::ConstPtr(property.type());
+
+
if (!function.returnType) {
diagnose(u"Cannot resolve property type %1 for binding on %2"_qs.arg(
property.typeName(), propertyName),
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 4d9bf6ef29..e2cac15684 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -271,11 +271,29 @@ void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import,
for (auto const &dependency : qAsConst(import.dependencies))
importHelper(dependency.module, types, QString(), dependency.version, true);
+ bool hasOptionalImports = false;
for (auto const &import : qAsConst(import.imports)) {
+ if (import.flags & QQmlDirParser::Import::Optional) {
+ hasOptionalImports = true;
+ if (!m_useOptionalImports) {
+ continue;
+ }
+
+ if (!(import.flags & QQmlDirParser::Import::OptionalDefault))
+ continue;
+ }
+
importHelper(import.module, types, isDependency ? QString() : prefix,
(import.flags & QQmlDirParser::Import::Auto) ? version : import.version,
isDependency);
}
+
+ if (hasOptionalImports && !m_useOptionalImports) {
+ m_warnings.append(
+ { u"%1 uses optional imports which are not supported. Some types might not be found."_qs
+ .arg(import.name),
+ QtCriticalMsg, QQmlJS::SourceLocation() });
+ }
}
static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h
index 6198717bed..cacc8eeb9b 100644
--- a/src/qmlcompiler/qqmljsimporter_p.h
+++ b/src/qmlcompiler/qqmljsimporter_p.h
@@ -50,10 +50,12 @@ class QQmlJSImporter
public:
using ImportedTypes = QHash<QString, QQmlJSImportedScope>;
- QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper)
- : m_importPaths(importPaths)
- , m_builtins({})
- , m_mapper(mapper)
+ QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
+ bool useOptionalImports = false)
+ : m_importPaths(importPaths),
+ m_builtins({}),
+ m_mapper(mapper),
+ m_useOptionalImports(useOptionalImports)
{}
QQmlJSResourceFileMapper *resourceFileMapper() { return m_mapper; }
@@ -153,6 +155,7 @@ private:
QList<QQmlJS::DiagnosticMessage> m_warnings;
AvailableTypes m_builtins;
QQmlJSResourceFileMapper *m_mapper = nullptr;
+ bool m_useOptionalImports;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 5e4c2e5b9a..c482b6597c 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -124,6 +124,7 @@ void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QS
m_currentScope = QQmlJSScope::create(type, m_currentScope);
setScopeName(m_currentScope, type, name);
m_currentScope->setIsComposite(true);
+ m_currentScope->setFilePath(QFileInfo(m_logger->fileName()).absoluteFilePath());
m_currentScope->setSourceLocation(location);
m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
}
@@ -220,13 +221,13 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
if (doRequeue)
continue;
if (foundProperty) {
- m_logger->logWarning(QStringLiteral("Cannot deduce type of alias \"%1\"")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot deduce type of alias \"%1\"")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
} else {
- m_logger->logWarning(QStringLiteral("Cannot resolve alias \"%1\"")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
}
} else {
property.setType(type);
@@ -279,9 +280,9 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
for (const auto &property : properties) {
if (!property.isAlias() || property.type())
continue;
- m_logger->logWarning(QStringLiteral("Alias \"%1\" is part of an alias cycle")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Alias \"%1\" is part of an alias cycle")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
}
}
}
@@ -315,9 +316,9 @@ void QQmlJSImportVisitor::processImportWarnings(
if (warnings.isEmpty())
return;
- m_logger->logWarning(QStringLiteral("Warnings occurred while importing %1:").arg(what),
- Log_Import, srcLocation);
- m_logger->processMessages(warnings, QtWarningMsg, Log_Import);
+ m_logger->log(QStringLiteral("Warnings occurred while importing %1:").arg(what), Log_Import,
+ srcLocation);
+ m_logger->processMessages(warnings, Log_Import);
}
void QQmlJSImportVisitor::importBaseModules()
@@ -411,11 +412,11 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
unusedImports.remove(import);
for (const auto &import : unusedImports) {
- m_logger->logInfo(QString::fromLatin1("Unused import at %1:%2:%3")
- .arg(m_logger->fileName())
- .arg(import.startLine)
- .arg(import.startColumn),
- Log_UnusedImport, import);
+ m_logger->log(QString::fromLatin1("Unused import at %1:%2:%3")
+ .arg(m_logger->fileName())
+ .arg(import.startLine)
+ .arg(import.startColumn),
+ Log_UnusedImport, import);
}
}
@@ -519,9 +520,8 @@ void QQmlJSImportVisitor::processDefaultProperties()
}
if (!isComponent) {
- m_logger->logWarning(
- QStringLiteral("Cannot assign to non-existent default property"),
- Log_Property, it.value().constFirst()->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot assign to non-existent default property"),
+ Log_Property, it.value().constFirst()->sourceLocation());
}
continue;
@@ -530,7 +530,7 @@ void QQmlJSImportVisitor::processDefaultProperties()
const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
if (it.value().length() > 1 && !defaultProp.isList()) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("Cannot assign multiple objects to a default non-list property"),
Log_Property, it.value().constFirst()->sourceLocation());
}
@@ -554,9 +554,8 @@ void QQmlJSImportVisitor::processDefaultProperties()
continue;
}
- m_logger->logWarning(
- QStringLiteral("Cannot assign to default property of incompatible type"),
- Log_Property, scope->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot assign to default property of incompatible type"),
+ Log_Property, scope->sourceLocation());
}
}
}
@@ -572,10 +571,9 @@ void QQmlJSImportVisitor::processPropertyTypes()
property.setType(propertyType);
type.scope->addOwnProperty(property);
} else {
- m_logger->logWarning(
- property.typeName()
- + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import, type.location);
+ m_logger->log(property.typeName()
+ + QStringLiteral(" was not found. Did you add all import paths?"),
+ Log_Import, type.location);
}
}
}
@@ -634,31 +632,28 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
if (objectBinding.onToken) {
if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueInterceptor"))) {
if (foundInterceptors.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Duplicate interceptor on property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Duplicate interceptor on property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else {
foundInterceptors.insert(uniqueBindingId);
}
} else if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueSource"))) {
if (foundValueSources.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Duplicate value source on property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Duplicate value source on property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else if (foundObjects.contains(uniqueBindingId)
|| foundLiterals.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Cannot combine value source and binding on "
- "property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Cannot combine value source and binding on "
+ "property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else {
foundValueSources.insert(uniqueBindingId);
}
} else {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("On-binding for property \"%1\" has wrong type \"%2\"")
.arg(propertyName)
.arg(typeName),
@@ -667,7 +662,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
} else {
// TODO: Warn here if binding.hasValue() is true
if (foundValueSources.contains(uniqueBindingId)) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
.arg(propertyName),
@@ -680,28 +675,27 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
// If the current scope is not fully resolved we cannot tell whether the property exists
// or not (we already warn elsewhere)
} else if (!property.isValid()) {
- m_logger->logWarning(QStringLiteral("Property \"%1\" is invalid or does not exist")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" is invalid or does not exist")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else if (property.type().isNull() || !property.type()->isFullyResolved()) {
// Property type is not fully resolved we cannot tell any more than this
- m_logger->logWarning(
- QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
- "missing an import.")
- .arg(propertyName)
- .arg(property.typeName()),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
+ "missing an import.")
+ .arg(propertyName)
+ .arg(property.typeName()),
+ Log_Property, objectBinding.location);
} else if (!childScope->isFullyResolved()) {
// If the childScope type is not fully resolved we cannot tell whether the type is
// incompatible (we already warn elsewhere)
} else {
// the type is incompatible
- m_logger->logWarning(QStringLiteral("Property \"%1\" of type \"%2\" is assigned an "
- "incompatible type \"%3\"")
- .arg(propertyName)
- .arg(property.typeName())
- .arg(getScopeName(childScope, QQmlJSScope::QMLScope)),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" of type \"%2\" is assigned an "
+ "incompatible type \"%3\"")
+ .arg(propertyName)
+ .arg(property.typeName())
+ .arg(getScopeName(childScope, QQmlJSScope::QMLScope)),
+ Log_Property, objectBinding.location);
}
}
}
@@ -710,7 +704,7 @@ void QQmlJSImportVisitor::checkRequiredProperties()
{
for (const auto &required : m_requiredProperties) {
if (!required.scope->hasProperty(required.name)) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("Property \"%1\" was marked as required but does not exist.")
.arg(required.name),
Log_Required, required.location);
@@ -764,24 +758,45 @@ void QQmlJSImportVisitor::checkRequiredProperties()
if (propertyUsedInRootAlias)
continue;
- const QString propertyScopeName = scopesToSearch.length() > 1
- ? getScopeName(scopesToSearch.at(scopesToSearch.length() - 2),
- QQmlJSScope::QMLScope)
+ const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.length() > 1
+ ? scopesToSearch.at(scopesToSearch.length() - 2)
+ : QQmlJSScope::ConstPtr();
+
+ const QString propertyScopeName = !propertyScope.isNull()
+ ? getScopeName(propertyScope, QQmlJSScope::QMLScope)
: u"here"_qs;
+
const QString requiredScopeName = prevRequiredScope
? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
: u"here"_qs;
+ std::optional<FixSuggestion> suggestion;
+
QString message =
QStringLiteral(
"Component is missing required property %1 from %2")
.arg(propName)
.arg(propertyScopeName);
- if (requiredScope != scope)
- message += QStringLiteral(" (marked as required by %3)")
- .arg(requiredScopeName);
+ if (requiredScope != scope) {
+ if (!prevRequiredScope.isNull()) {
+ auto sourceScope = prevRequiredScope->baseType();
+ suggestion = FixSuggestion {
+ { { u"%1:%2:%3: Property marked as required in %4"_qs
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation(), QString(),
+ sourceScope->filePath() } }
+ };
+ } else {
+ message += QStringLiteral(" (marked as required by %1)")
+ .arg(requiredScopeName);
+ }
+ }
- m_logger->logWarning(message, Log_Required, defScope->sourceLocation());
+ m_logger->log(message, Log_Required, defScope->sourceLocation(), true,
+ true, suggestion);
}
}
prevRequiredScope = requiredScope;
@@ -816,22 +831,20 @@ void QQmlJSImportVisitor::processPropertyBindings()
}
}
- m_logger->logWarning(
- QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
- "exists in the current element.")
- .arg(name),
- Log_Type, location, true, true, fixSuggestion);
+ m_logger->log(QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
+ "exists in the current element.")
+ .arg(name),
+ Log_Type, location, true, true, fixSuggestion);
continue;
}
const auto property = scope->property(name);
if (!property.type()) {
- m_logger->logWarning(
- QStringLiteral("No type found for property \"%1\". This may be due "
- "to a missing import statement or incomplete "
- "qmltypes files.")
- .arg(name),
- Log_Type, location);
+ m_logger->log(QStringLiteral("No type found for property \"%1\". This may be due "
+ "to a missing import statement or incomplete "
+ "qmltypes files.")
+ .arg(name),
+ Log_Type, location);
}
const auto &annotations = property.annotations();
@@ -849,7 +862,7 @@ void QQmlJSImportVisitor::processPropertyBindings()
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, location);
+ m_logger->log(message, Log_Deprecation, location);
}
}
}
@@ -928,9 +941,9 @@ void QQmlJSImportVisitor::checkSignals()
.arg(pair.first, pair.second.join(u", ")) } } };
}
- m_logger->logWarning(QStringLiteral("no matching signal found for handler \"%1\"")
- .arg(pair.first),
- Log_UnqualifiedAccess, location, true, true, fix);
+ m_logger->log(QStringLiteral("no matching signal found for handler \"%1\"")
+ .arg(pair.first),
+ Log_UnqualifiedAccess, location, true, true, fix);
continue;
}
@@ -938,10 +951,10 @@ void QQmlJSImportVisitor::checkSignals()
const QStringList signalParameters = signalMethod->parameterNames();
if (pair.second.length() > signalParameters.length()) {
- m_logger->logWarning(QStringLiteral("Signal handler for \"%2\" has more formal"
- " parameters than the signal it handles.")
- .arg(pair.first),
- Log_Signal, location);
+ m_logger->log(QStringLiteral("Signal handler for \"%2\" has more formal"
+ " parameters than the signal it handles.")
+ .arg(pair.first),
+ Log_Signal, location);
continue;
}
@@ -951,13 +964,13 @@ void QQmlJSImportVisitor::checkSignals()
if (j == i || j < 0)
continue;
- m_logger->logWarning(QStringLiteral("Parameter %1 to signal handler for \"%2\""
- " is called \"%3\". The signal has a parameter"
- " of the same name in position %4.")
- .arg(i + 1)
- .arg(pair.first, handlerParameter)
- .arg(j + 1),
- Log_Signal, location);
+ m_logger->log(QStringLiteral("Parameter %1 to signal handler for \"%2\""
+ " is called \"%3\". The signal has a parameter"
+ " of the same name in position %4.")
+ .arg(i + 1)
+ .arg(pair.first, handlerParameter)
+ .arg(j + 1),
+ Log_Signal, location);
}
}
}
@@ -1014,10 +1027,10 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina
inheritenceCycle.append(seen->baseTypeName());
}
- m_logger->logWarning(QStringLiteral("%1 is part of an inheritance cycle: %2")
- .arg(scope->internalName())
- .arg(inheritenceCycle),
- Log_InheritanceCycle);
+ m_logger->log(QStringLiteral("%1 is part of an inheritance cycle: %2")
+ .arg(scope->internalName())
+ .arg(inheritenceCycle),
+ Log_InheritanceCycle, scope->sourceLocation());
originalScope->clearBaseType();
break;
}
@@ -1026,12 +1039,11 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina
const auto newScope = scope->baseType();
if (newScope.isNull() && !scope->baseTypeName().isEmpty()) {
- m_logger->logWarning(
- scope->baseTypeName()
- + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import, scope->sourceLocation(), true, true,
- QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(),
- scope->sourceLocation()));
+ m_logger->log(scope->baseTypeName()
+ + QStringLiteral(" was not found. Did you add all import paths?"),
+ Log_Import, scope->sourceLocation(), true, true,
+ QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(),
+ scope->sourceLocation()));
}
scope = newScope;
@@ -1051,7 +1063,7 @@ void QQmlJSImportVisitor::checkDeprecation(const QQmlJSScope::ConstPtr &original
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, originalScope->sourceLocation());
+ m_logger->log(message, Log_Deprecation, originalScope->sourceLocation());
}
}
}
@@ -1072,12 +1084,12 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc
case QQmlJSScope::GroupedPropertyScope:
case QQmlJSScope::AttachedPropertyScope:
if (!childScope->baseType()) {
- m_logger->logWarning(QStringLiteral("unknown %1 property scope %2.")
- .arg(type == QQmlJSScope::GroupedPropertyScope
- ? QStringLiteral("grouped")
- : QStringLiteral("attached"),
- childScope->internalName()),
- Log_UnqualifiedAccess, childScope->sourceLocation());
+ m_logger->log(QStringLiteral("unknown %1 property scope %2.")
+ .arg(type == QQmlJSScope::GroupedPropertyScope
+ ? QStringLiteral("grouped")
+ : QStringLiteral("attached"),
+ childScope->internalName()),
+ Log_UnqualifiedAccess, childScope->sourceLocation());
}
children.append(childScope->childScopes());
default:
@@ -1121,10 +1133,36 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl)
if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
|| s.contains(QChar(0x2029u))) {
- m_logger->logWarning(QStringLiteral("String contains unescaped line terminator which is "
- "deprecated. Use a template "
- "literal instead."),
- Log_MultilineString, sl->literalToken);
+ QString templateString;
+
+ bool escaped = false;
+ const QChar stringQuote = s[0];
+ for (qsizetype i = 1; i < s.length() - 1; i++) {
+ const QChar c = s[i];
+
+ if (c == u'\\') {
+ escaped = !escaped;
+ } else if (escaped) {
+ // If we encounter an escaped quote, unescape it since we use backticks here
+ if (c == stringQuote)
+ templateString.chop(1);
+
+ escaped = false;
+ } else {
+ if (c == u'`')
+ templateString += u'\\';
+ if (c == u'$' && i + 1 < s.length() - 1 && s[i + 1] == u'{')
+ templateString += u'\\';
+ }
+
+ templateString += c;
+ }
+
+ const FixSuggestion suggestion = { { { u"Use a template literal instead"_qs,
+ sl->literalToken, u"`" + templateString + u"`" } } };
+ m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
+ "deprecated."),
+ Log_MultilineString, sl->literalToken, true, true, suggestion);
}
return true;
@@ -1181,8 +1219,8 @@ void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
{
if (!m_inlineComponentName.isNull()) {
- m_logger->logWarning(u"Nested inline components are not supported"_qs, Log_Syntax,
- component->firstSourceLocation());
+ m_logger->log(u"Nested inline components are not supported"_qs, Log_Syntax,
+ component->firstSourceLocation());
return true;
}
@@ -1234,9 +1272,9 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (const auto idExpression = cast<IdentifierExpression *>(node)) {
aliasExpr.prepend(idExpression->name.toString());
} else {
- m_logger->logWarning(QStringLiteral("Invalid alias expression. Only IDs and field "
- "member expressions can be aliased."),
- Log_Alias, expression->firstSourceLocation());
+ m_logger->log(QStringLiteral("Invalid alias expression. Only IDs and field "
+ "member expressions can be aliased."),
+ Log_Alias, expression->firstSourceLocation());
}
} else {
const auto name = publicMember->memberType->name.toString();
@@ -1270,7 +1308,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (publicMember->isRequired())
m_currentScope->setPropertyLocallyRequired(prop.propertyName(), true);
- parseLiteralBinding(publicMember->name.toString(), publicMember->statement);
+ parseLiteralOrScriptBinding(publicMember->name.toString(), publicMember->statement);
break;
}
@@ -1366,8 +1404,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
{
- m_logger->logWarning(u"Declared function \"%1\""_qs.arg(fdecl->name), Log_ControlsSanity,
- fdecl->firstSourceLocation());
+ m_logger->log(u"Declared function \"%1\""_qs.arg(fdecl->name), Log_ControlsSanity,
+ fdecl->firstSourceLocation());
visitFunctionExpressionHelper(fdecl);
return true;
}
@@ -1394,12 +1432,9 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
// ### TODO: add warning about suspicious translation binding when returning false?
-static std::optional<QQmlJSMetaPropertyBinding>
-handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
- const QQmlJSImporter::ImportedTypes &rootScopeImports,
- const QQmlJS::SourceLocation &location)
+void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base,
+ QQmlJS::AST::ArgumentList *args)
{
- std::optional<QQmlJSMetaPropertyBinding> maybeBinding = std::nullopt;
QStringView mainString;
auto registerMainString = [&](QStringView string) {
mainString = string;
@@ -1408,116 +1443,75 @@ handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
auto discardCommentString = [](QStringView) {return -1;};
auto finalizeBinding = [&](QV4::CompiledData::Binding::ValueType type,
QV4::CompiledData::TranslationData) {
- QQmlJSMetaPropertyBinding binding(location);
if (type == QV4::CompiledData::Binding::Type_Translation) {
binding.setTranslation(mainString);
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
binding.setTranslationId(mainString);
} else {
- binding.setLiteral(
- QQmlJSMetaPropertyBinding::StringLiteral, u"string"_qs,
- mainString.toString(), rootScopeImports[u"string"_qs].scope);
+ binding.setStringLiteral(mainString);
}
- maybeBinding = binding;
};
QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString, discardCommentString, finalizeBinding);
- return maybeBinding;
}
-bool QQmlJSImportVisitor::parseLiteralBinding(const QString name,
+QQmlJSImportVisitor::LiteralOrScriptParseResult QQmlJSImportVisitor::parseLiteralOrScriptBinding(const QString name,
const QQmlJS::AST::Statement *statement)
{
const auto *exprStatement = cast<const ExpressionStatement *>(statement);
if (exprStatement == nullptr)
- return false;
-
- QVariant value;
- QString literalType;
- QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid;
+ return LiteralOrScriptParseResult::Invalid;
auto expr = exprStatement->expression;
+ QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
+
switch (expr->kind) {
case Node::Kind_TrueLiteral:
- value = true;
- literalType = u"bool"_qs;
- bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
+ binding.setBoolLiteral(true);
break;
case Node::Kind_FalseLiteral:
- value = false;
- literalType = u"bool"_qs;
- bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
+ binding.setBoolLiteral(false);
break;
case Node::Kind_NullExpression:
- // Note: because we set value to nullptr, Null binding returns false in
- // QQmlJSMetaPropertyBinding::hasLiteral()
- value = QVariant::fromValue(nullptr);
- Q_ASSERT(value.isNull());
- literalType = u"$internal$.std::nullptr_t"_qs;
- bindingType = QQmlJSMetaPropertyBinding::Null;
+ binding.setNullLiteral();
break;
case Node::Kind_NumericLiteral:
- literalType = u"double"_qs;
- value = cast<NumericLiteral *>(expr)->value;
- bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
+ binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
break;
case Node::Kind_StringLiteral:
- literalType = u"string"_qs;
- value = cast<StringLiteral *>(expr)->value.toString();
- bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
+ binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
break;
case Node::Kind_RegExpLiteral:
- literalType = u"regexp"_qs;
- value = cast<RegExpLiteral *>(expr)->pattern.toString();
- bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral;
+ binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
break;
case Node::Kind_TemplateLiteral: {
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
Q_ASSERT(templateLit);
- value = templateLit->value.toString();
if (templateLit->hasNoSubstitution) {
- literalType = u"string"_qs;
- bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
+ binding.setStringLiteral(templateLit->value);
} else {
- bindingType = QQmlJSMetaPropertyBinding::Script;
+ binding.setScriptBinding();
}
break;
}
default:
if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
- if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
- literalType = u"double"_qs;
- bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
- value = -lit->value;
- }
+ if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
+ binding.setNumberLiteral(-lit->value);
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
- if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
- if (auto translationBindingOpt = handleTranslationBinding(
- base->name, call->arguments, m_rootScopeImports,
- expr->firstSourceLocation())) {
- auto translationBinding = translationBindingOpt.value();
- translationBinding.setPropertyName(name);
- m_currentScope->addOwnPropertyBinding(translationBinding);
- if (translationBinding.bindingType() == QQmlJSMetaPropertyBinding::BindingType::StringLiteral)
- m_literalScopesToCheck << m_currentScope;
- return true;
- }
- }
+ if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
+ handleTranslationBinding(binding, base->name, call->arguments);
}
break;
}
-
- if (!QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType))
- return false;
- Q_ASSERT(m_rootScopeImports.contains(literalType)); // built-ins must contain support for all literal bindings
-
- QQmlJSMetaPropertyBinding binding(exprStatement->expression->firstSourceLocation(), name);
- binding.setLiteral(bindingType, literalType, value, m_rootScopeImports[literalType].scope);
-
- m_currentScope->addOwnPropertyBinding(binding);
-
+ if (binding.isValid()) // always add the binding to the scope, even if it's not a literal one
+ m_currentScope->addOwnPropertyBinding(binding);
+ else
+ return LiteralOrScriptParseResult::Invalid;
+ if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
+ return LiteralOrScriptParseResult::Script;
m_literalScopesToCheck << m_currentScope;
- return true;
+ return LiteralOrScriptParseResult::Literal;
}
void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
@@ -1527,10 +1521,12 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
if (const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
return idExpression->name.toString();
else if (const auto *idString = cast<StringLiteral *>(statement->expression)) {
- m_logger->logInfo(u"ids do not need quotation marks"_qs, Log_Syntax, idString->firstSourceLocation());
+ m_logger->log(u"ids do not need quotation marks"_qs, Log_SyntaxIdQuotation,
+ idString->firstSourceLocation());
return idString->value.toString();
}
- m_logger->logWarning(u"Failed to parse id"_qs, Log_Syntax, statement->expression->firstSourceLocation());
+ m_logger->log(u"Failed to parse id"_qs, Log_Syntax,
+ statement->expression->firstSourceLocation());
return QString();
}();
@@ -1542,14 +1538,11 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
auto otherLocation = otherScopeWithID->sourceLocation();
// critical because subsequent analysis cannot cope with messed up ids
// and the file is invalid
- m_logger->logCritical(
- u"Found a duplicated id. id %1 was first declared at %2:%3"_qs.arg(
- name,
- QString::number(otherLocation.startLine),
- QString::number(otherLocation.startColumn)),
- Log_Syntax, // ??
- scriptBinding->firstSourceLocation()
- );
+ m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_qs.arg(
+ name, QString::number(otherLocation.startLine),
+ QString::number(otherLocation.startColumn)),
+ Log_Syntax, // ??
+ scriptBinding->firstSourceLocation());
}
}
if (!name.isEmpty())
@@ -1580,19 +1573,15 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
auto name = group->name;
if (id && id->name.toString() == u"anchors")
- m_logger->logWarning(u"Using anchors here"_qs, Log_ControlsSanity,
- scriptBinding->firstSourceLocation());
+ m_logger->log(u"Using anchors here"_qs, Log_ControlsSanity,
+ scriptBinding->firstSourceLocation());
const auto signal = QQmlJSUtils::signalName(name);
if (!signal.has_value()) {
m_propertyBindings[m_currentScope].append(
{ m_savedBindingOuterScope, group->firstSourceLocation(), name.toString() });
- if (!parseLiteralBinding(name.toString(), scriptBinding->statement)) {
- QQmlJSMetaPropertyBinding binding(group->firstSourceLocation(), name.toString());
- // TODO: Actually store the value
- m_savedBindingOuterScope->addOwnPropertyBinding(binding);
- }
+ parseLiteralOrScriptBinding(name.toString(), scriptBinding->statement);
} else {
const auto statement = scriptBinding->statement;
QStringList signalParameters;
@@ -1604,8 +1593,8 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
}
}
- m_logger->logWarning(u"Declared signal handler \"%1\""_qs.arg(name), Log_ControlsSanity,
- scriptBinding->firstSourceLocation());
+ m_logger->log(u"Declared signal handler \"%1\""_qs.arg(name), Log_ControlsSanity,
+ scriptBinding->firstSourceLocation());
m_signals[m_currentScope].append({ m_savedBindingOuterScope, group->firstSourceLocation(),
qMakePair(name.toString(), signalParameters) });
@@ -1803,10 +1792,14 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
{
- // If a file uses pragma Strict it expects to be compiled, so automatically enable compiler
- // warnings unless the user has explicitly set the level.
- if (pragma->name == u"Strict"_qs && !m_logger->wasCategoryChanged(Log_Compiler))
+ // If a file uses pragma Strict, it expects to be compiled, so automatically
+ // enable compiler warnings unless the level is set explicitly already (e.g.
+ // by the user).
+ if (pragma->name == u"Strict"_qs && !m_logger->wasCategoryChanged(Log_Compiler)) {
+ // TODO: the logic here is rather complicated and may be buggy
m_logger->setCategoryLevel(Log_Compiler, QtWarningMsg);
+ m_logger->setCategoryIgnored(Log_Compiler, false);
+ }
if (pragma->name == u"Singleton")
m_rootIsSingleton = true;
@@ -1816,8 +1809,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
void QQmlJSImportVisitor::throwRecursionDepthError()
{
- m_logger->logCritical(QStringLiteral("Maximum statement or expression depth exceeded"),
- Log_RecursionDepthError);
+ m_logger->log(QStringLiteral("Maximum statement or expression depth exceeded"),
+ Log_RecursionDepthError, QQmlJS::SourceLocation());
}
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
@@ -1906,11 +1899,10 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("with"),
ast->firstSourceLocation());
- m_logger->logWarning(
- QStringLiteral("with statements are strongly discouraged in QML "
- "and might cause false positives when analysing unqualified "
- "identifiers"),
- Log_WithStatement, ast->firstSourceLocation());
+ m_logger->log(QStringLiteral("with statements are strongly discouraged in QML "
+ "and might cause false positives when analysing unqualified "
+ "identifiers"),
+ Log_WithStatement, ast->firstSourceLocation());
return true;
}
@@ -2031,7 +2023,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
}
if (foundIds) {
- m_logger->logWarning(
+ m_logger->log(
u"Cannot defer property assignment to \"%1\". Assigning an id to an object or one of its sub-objects bound to a deferred property will make the assignment immediate."_qs
.arg(propertyName),
Log_DeferredPropertyId, uiob->firstSourceLocation());
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 4a006c9cde..e932bb5b2f 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -203,7 +203,8 @@ protected:
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
QQmlJSLogger *m_logger;
- bool parseLiteralBinding(const QString name, const QQmlJS::AST::Statement *statement);
+ enum class LiteralOrScriptParseResult { Invalid, Script, Literal };
+ LiteralOrScriptParseResult parseLiteralOrScriptBinding(const QString name, const QQmlJS::AST::Statement *statement);
// Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index c50740750e..4d3eb8e9b5 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -56,7 +56,7 @@ public:
QQmlJS::SourceLocation accessLocation) override
{
Q_UNUSED(fileName)
- m_logger->logWarning(
+ m_logger->log(
u"Variable \"%1\" is used here before its declaration. The declaration is at %2:%3."_qs
.arg(name)
.arg(declarationLocation.startLine)
@@ -69,7 +69,7 @@ private:
};
QQmlJSLinter::QQmlJSLinter(const QStringList &importPaths, bool useAbsolutePath)
- : m_useAbsolutePath(useAbsolutePath), m_importer(importPaths, nullptr)
+ : m_useAbsolutePath(useAbsolutePath), m_importer(importPaths, nullptr, true)
{
}
@@ -101,8 +101,8 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
if (option != logger->options().constEnd())
categories << option->m_category;
else
- logger->logWarning(u"qmllint directive on unknown category \"%1\""_qs.arg(category),
- Log_Syntax, loc);
+ logger->log(u"qmllint directive on unknown category \"%1\""_qs.arg(category),
+ Log_Syntax, loc);
}
if (categories.isEmpty()) {
@@ -129,8 +129,8 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
} else if (command == u"enable"_qs) {
enablesPerLine[loc.startLine + 1] |= categories;
} else {
- logger->logWarning(u"Invalid qmllint directive \"%1\" provided"_qs.arg(command),
- Log_Syntax, loc);
+ logger->log(u"Invalid qmllint directive \"%1\" provided"_qs.arg(command), Log_Syntax,
+ loc);
}
}
@@ -221,6 +221,7 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
jsonFix[u"charOffset"_qs] = static_cast<int>(fix.cutLocation.offset);
jsonFix[u"length"_qs] = static_cast<int>(fix.cutLocation.length);
jsonFix[u"replacement"_qs] = fix.replacementString;
+ jsonFix[u"isHint"] = fix.isHint;
suggestions << jsonFix;
}
}
@@ -314,7 +315,7 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
if (!it.value().m_changed)
continue;
- m_logger->setCategoryError(it.value().m_category, it.value().m_error);
+ m_logger->setCategoryIgnored(it.value().m_category, it.value().m_ignored);
m_logger->setCategoryLevel(it.value().m_category, it.value().m_level);
}
@@ -361,10 +362,9 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
QList<QQmlJS::DiagnosticMessage> warnings = m_importer.takeGlobalWarnings();
if (!warnings.isEmpty()) {
- m_logger->logWarning(
- QStringLiteral("Type warnings occurred while evaluating file:"),
- Log_Import);
- m_logger->processMessages(warnings, QtWarningMsg, Log_Import);
+ m_logger->log(QStringLiteral("Type warnings occurred while evaluating file:"),
+ Log_Import, QQmlJS::SourceLocation());
+ m_logger->processMessages(warnings, Log_Import);
}
success &= !m_logger->hasWarnings() && !m_logger->hasErrors();
diff --git a/src/qmlcompiler/qqmljsloadergenerator.cpp b/src/qmlcompiler/qqmljsloadergenerator.cpp
index ffe4774266..f0e28895e9 100644
--- a/src/qmlcompiler/qqmljsloadergenerator.cpp
+++ b/src/qmlcompiler/qqmljsloadergenerator.cpp
@@ -125,6 +125,8 @@ bool qQmlJSGenerateLoader(const QStringList &compiledFiles, const QString &outpu
stream << "#include <QtQml/qqmlprivate.h>\n";
stream << "#include <QtCore/qdir.h>\n";
stream << "#include <QtCore/qurl.h>\n";
+ stream << "#include <QtCore/qhash.h>\n";
+ stream << "#include <QtCore/qstring.h>\n";
stream << "\n";
stream << "namespace QmlCacheGeneratedCode {\n";
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index d7bdc714a6..ce2f559c1a 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -28,16 +28,19 @@
#include "qqmljslogger_p.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+
QT_BEGIN_NAMESPACE
const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
static QMap<QString, QQmlJSLogger::Option> optionsMap = {
{ QStringLiteral("required"),
QQmlJSLogger::Option(Log_Required, QStringLiteral("RequiredProperty"),
- QStringLiteral("Warn about required properties"), QtInfoMsg) },
+ QStringLiteral("Warn about required properties"), QtWarningMsg) },
{ QStringLiteral("alias"),
QQmlJSLogger::Option(Log_Alias, QStringLiteral("PropertyAlias"),
- QStringLiteral("Warn about alias errors"), QtInfoMsg) },
+ QStringLiteral("Warn about alias errors"), QtWarningMsg) },
{ QStringLiteral("import"),
QQmlJSLogger::Option(Log_Import, QStringLiteral("ImportFailure"),
QStringLiteral("Warn about failing imports and deprecated qmltypes"),
@@ -49,7 +52,7 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("inheritance-cycle"),
QQmlJSLogger::Option(Log_InheritanceCycle, QStringLiteral("InheritanceCycle"),
- QStringLiteral("Warn about inheritance cycles"), QtInfoMsg) },
+ QStringLiteral("Warn about inheritance cycles"), QtWarningMsg) },
{ QStringLiteral("deprecated"),
QQmlJSLogger::Option(Log_Deprecation, QStringLiteral("Deprecated"),
QStringLiteral("Warn about deprecated properties and types"),
@@ -64,13 +67,13 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("property"),
QQmlJSLogger::Option(Log_Property, QStringLiteral("UnknownProperty"),
- QStringLiteral("Warn about unknown properties"), QtInfoMsg) },
+ QStringLiteral("Warn about unknown properties"), QtWarningMsg) },
{ QStringLiteral("deferred-property-id"),
QQmlJSLogger::Option(
Log_DeferredPropertyId, QStringLiteral("DeferredPropertyId"),
QStringLiteral(
"Warn about making deferred properties immediate by giving them an id."),
- QtInfoMsg) },
+ QtWarningMsg) },
{ QStringLiteral("unqualified"),
QQmlJSLogger::Option(
Log_UnqualifiedAccess, QStringLiteral("UnqualifiedAccess"),
@@ -78,24 +81,23 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("unused-imports"),
QQmlJSLogger::Option(Log_UnusedImport, QStringLiteral("UnusedImports"),
- QStringLiteral("Warn about unused imports"), QtInfoMsg, false) },
+ QStringLiteral("Warn about unused imports"), QtInfoMsg) },
{ QStringLiteral("multiline-strings"),
QQmlJSLogger::Option(Log_MultilineString, QStringLiteral("MultilineStrings"),
- QStringLiteral("Warn about multiline strings"), QtInfoMsg, false) },
+ QStringLiteral("Warn about multiline strings"), QtInfoMsg) },
{ QStringLiteral("compiler"),
QQmlJSLogger::Option(Log_Compiler, QStringLiteral("CompilerWarnings"),
- QStringLiteral("Warn about compiler issues"), QtCriticalMsg,
- false) },
+ QStringLiteral("Warn about compiler issues"), QtCriticalMsg, true) },
{ QStringLiteral("controls-sanity"),
QQmlJSLogger::Option(
Log_ControlsSanity, QStringLiteral("ControlsSanity"),
QStringLiteral("Performance checks used for QuickControl's implementation"),
- QtCriticalMsg, false) },
+ QtCriticalMsg, true) },
{ QStringLiteral("multiple-attached-objects"),
QQmlJSLogger::Option(
Log_AttachedPropertyReuse, QStringLiteral("AttachedPropertyReuse"),
QStringLiteral("Warn if attached types from parent components aren't reused"),
- QtCriticalMsg, false) }
+ QtCriticalMsg, true) }
};
return optionsMap;
@@ -106,20 +108,20 @@ QQmlJSLogger::QQmlJSLogger()
const auto &opt = options();
for (auto it = opt.cbegin(); it != opt.cend(); ++it) {
m_categoryLevels[it.value().m_category] = it.value().m_level;
- m_categoryError[it.value().m_category] = it.value().m_error;
+ m_categoryIgnored[it.value().m_category] = it.value().m_ignored;
}
// These have to be set up manually since we don't expose it as an option
- m_categoryLevels[Log_RecursionDepthError] = QtInfoMsg;
- m_categoryError[Log_RecursionDepthError] = true;
- m_categoryLevels[Log_Syntax] = QtInfoMsg;
- m_categoryError[Log_Syntax] = true;
+ m_categoryLevels[Log_RecursionDepthError] = QtCriticalMsg;
+ m_categoryLevels[Log_Syntax] = QtWarningMsg; // TODO: because we usually report it as a warning!
+ m_categoryLevels[Log_SyntaxIdQuotation] = QtWarningMsg;
+ m_categoryLevels[Log_SyntaxDuplicateIds] = QtCriticalMsg;
// setup color output
m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground);
- m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground);
+ m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow?
m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground);
- m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground);
+ m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None?
}
static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
@@ -134,18 +136,22 @@ static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
- bool showFileName, const std::optional<FixSuggestion> &suggestion)
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName)
{
- if (isMsgTypeLess(type, m_categoryLevels[category]))
+ if (isCategoryIgnored(category))
return;
+ // Note: assume \a type is the type we should prefer for logging
+
if (srcLocation.isValid() && m_ignoredWarnings[srcLocation.startLine].contains(category))
return;
QString prefix;
- if (!m_fileName.isEmpty() && showFileName)
- prefix = m_fileName + QStringLiteral(":");
+ if ((!overrideFileName.isEmpty() || !m_fileName.isEmpty()) && showFileName)
+ prefix =
+ (!overrideFileName.isEmpty() ? overrideFileName : m_fileName) + QStringLiteral(":");
if (srcLocation.isValid())
prefix += QStringLiteral("%1:%2:").arg(srcLocation.startLine).arg(srcLocation.startColumn);
@@ -153,26 +159,21 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
if (!prefix.isEmpty())
prefix.append(QLatin1Char(' '));
- m_output.writePrefixedMessage(prefix + message, type);
-
- QtMsgType machineType = isMsgTypeLess(QtWarningMsg, type) ? QtCriticalMsg : QtInfoMsg;
+ // Note: we do the clamping to [Info, Critical] range since our logger only
+ // supports 3 categories
+ type = std::clamp(type, QtInfoMsg, QtCriticalMsg, isMsgTypeLess);
- // If this is a category that produces error codes, we need to up all the messages to at least a
- // warning level
- if (isCategoryError(category)) {
- if (isMsgTypeLess(type, QtWarningMsg))
- machineType = QtWarningMsg;
- else
- machineType = type;
- }
+ // Note: since we clamped our \a type, the output message is not printed
+ // exactly like it was requested, bear with us
+ m_output.writePrefixedMessage(prefix + message, type);
Message diagMsg;
diagMsg.message = message;
diagMsg.loc = srcLocation;
- diagMsg.type = machineType;
+ diagMsg.type = type;
diagMsg.fixSuggestion = suggestion;
- switch (machineType) {
+ switch (type) {
case QtWarningMsg: m_warnings.push_back(diagMsg); break;
case QtCriticalMsg: m_errors.push_back(diagMsg); break;
case QtInfoMsg: m_infos.push_back(diagMsg); break;
@@ -180,29 +181,41 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
}
if (srcLocation.isValid() && !m_code.isEmpty() && showContext)
- printContext(srcLocation);
+ printContext(overrideFileName, srcLocation);
if (suggestion.has_value())
printFix(suggestion.value());
}
void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
- QtMsgType level, QQmlJSLoggerCategory category)
+ QQmlJSLoggerCategory category)
{
- if (isMsgTypeLess(level, m_categoryLevels[category]) || messages.isEmpty())
+ if (messages.isEmpty() || isCategoryIgnored(category))
return;
m_output.write(QStringLiteral("---\n"));
+ // TODO: we should instead respect message's category here (potentially, it
+ // should hold a category instead of type)
for (const QQmlJS::DiagnosticMessage &message : messages)
- logWarning(message.message, category, QQmlJS::SourceLocation(), false, false);
+ log(message.message, category, QQmlJS::SourceLocation(), false, false);
m_output.write(QStringLiteral("---\n\n"));
}
-void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
+void QQmlJSLogger::printContext(const QString &overrideFileName,
+ const QQmlJS::SourceLocation &location)
{
- IssueLocationWithContext issueLocationWithContext { m_code, location };
+ QString code = m_code;
+
+ if (!overrideFileName.isEmpty() && overrideFileName != QFileInfo(m_fileName).absolutePath()) {
+ QFile file(overrideFileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, location };
if (const QStringView beforeText = issueLocationWithContext.beforeText(); !beforeText.isEmpty())
m_output.write(beforeText);
@@ -226,28 +239,56 @@ void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
void QQmlJSLogger::printFix(const FixSuggestion &fix)
{
+ const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
+ QString code = m_code;
+ QString currentFile;
for (const auto &fixItem : fix.fixes) {
m_output.writePrefixedMessage(fixItem.message, QtInfoMsg);
if (!fixItem.cutLocation.isValid())
continue;
- IssueLocationWithContext issueLocationWithContext { m_code, fixItem.cutLocation };
+ if (fixItem.fileName == currentFile) {
+ // Nothing to do in this case, we've already read the code
+ } else if (fixItem.fileName.isEmpty() || fixItem.fileName == currentFileAbsPath) {
+ code = m_code;
+ } else {
+ QFile file(fixItem.fileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ currentFile = fixItem.fileName;
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, fixItem.cutLocation };
if (const QStringView beforeText = issueLocationWithContext.beforeText();
!beforeText.isEmpty()) {
m_output.write(beforeText);
}
- m_output.write(fixItem.replacementString, QtDebugMsg);
+ // The replacement string can be empty if we're only pointing something out to the user
+ QStringView replacementString = fixItem.replacementString.isEmpty()
+ ? issueLocationWithContext.issueText()
+ : fixItem.replacementString;
+
+ // But if there's nothing to change it has to be a hint
+ if (fixItem.replacementString.isEmpty())
+ Q_ASSERT(fixItem.isHint);
+
+ m_output.write(replacementString, QtDebugMsg);
m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
int tabCount = issueLocationWithContext.beforeText().count(u'\t');
+
+ // Do not draw location indicator for multiline replacement strings
+ if (replacementString.contains(u'\n'))
+ continue;
+
m_output.write(u" "_qs.repeated(
issueLocationWithContext.beforeText().length() - tabCount)
+ u"\t"_qs.repeated(tabCount)
- + u"^"_qs.repeated(fixItem.replacementString.length())
- + u'\n');
+ + u"^"_qs.repeated(fixItem.replacementString.length()) + u'\n');
}
}
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index 0792cd434b..2ddcb2ac5a 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -107,6 +107,8 @@ enum QQmlJSLoggerCategory {
Log_UnusedImport,
Log_MultilineString,
Log_Syntax,
+ Log_SyntaxIdQuotation,
+ Log_SyntaxDuplicateIds,
Log_Compiler,
Log_ControlsSanity,
Log_AttachedPropertyReuse,
@@ -120,6 +122,10 @@ struct FixSuggestion
QString message;
QQmlJS::SourceLocation cutLocation = QQmlJS::SourceLocation();
QString replacementString = QString();
+ QString fileName = QString();
+ // A Fix is a hint if it can not be automatically applied to fix an issue or only points out
+ // its origin
+ bool isHint = true;
};
QList<Fix> fixes;
};
@@ -137,31 +143,32 @@ public:
{
Option() = default;
Option(QQmlJSLoggerCategory category, QString settingsName, const QString &description,
- QtMsgType level, bool error = true)
+ QtMsgType level, bool ignored = false)
: m_category(category),
m_settingsName(settingsName),
m_description(description),
m_level(level),
- m_error(error)
+ m_ignored(ignored)
{
}
QQmlJSLoggerCategory m_category;
QString m_settingsName;
QString m_description;
QtMsgType m_level;
- bool m_error;
+ bool m_ignored;
bool m_changed = false;
QString levelToString() const {
+ // TODO:: this only makes sense to qmllint
+ Q_ASSERT(m_ignored || m_level != QtCriticalMsg);
+ if (m_ignored)
+ return QStringLiteral("disable");
+
switch (m_level) {
case QtInfoMsg:
- return m_error ? QStringLiteral("warning") : QStringLiteral("info");
+ return QStringLiteral("info");
case QtWarningMsg:
- // TODO: This case doesn't cleanly map onto any warning level yet
- // this has to be handled in the option overhaul
- return m_error ? QStringLiteral("warning") : QStringLiteral("info");
- case QtCriticalMsg:
- return QStringLiteral("disable");
+ return QStringLiteral("warning");
default:
Q_UNREACHABLE();
break;
@@ -170,14 +177,14 @@ public:
bool setLevel(const QString &level) {
if (level == QStringLiteral("disable")) {
- m_level = QtCriticalMsg;
- m_error = false;
+ m_level = QtCriticalMsg; // TODO: only so for consistency with previous logic
+ m_ignored = true;
} else if (level == QStringLiteral("info")) {
m_level = QtInfoMsg;
- m_error = false;
+ m_ignored = false;
} else if (level == QStringLiteral("warning")) {
- m_level = QtInfoMsg;
- m_error = true;
+ m_level = QtWarningMsg;
+ m_ignored = false;
} else {
return false;
}
@@ -206,10 +213,13 @@ public:
m_categoryChanged[category] = true;
}
- bool isCategoryError(QQmlJSLoggerCategory category) const { return m_categoryError[category]; }
- void setCategoryError(QQmlJSLoggerCategory category, bool error)
+ bool isCategoryIgnored(QQmlJSLoggerCategory category) const
+ {
+ return m_categoryIgnored[category];
+ }
+ void setCategoryIgnored(QQmlJSLoggerCategory category, bool error)
{
- m_categoryError[category] = error;
+ m_categoryIgnored[category] = error;
m_categoryChanged[category] = true;
}
@@ -218,31 +228,23 @@ public:
return m_categoryChanged[category];
}
- void logInfo(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
- {
- log(message, category, srcLocation, QtInfoMsg, showContext, showFileName, suggestion);
- }
+ /*! \internal
- void logWarning(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
- {
- log(message, category, srcLocation, QtWarningMsg, showContext, showFileName, suggestion);
- }
+ Logs \a message with severity deduced from \a category. Prefer using
+ this function in most cases.
- void logCritical(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
+ \sa setCategoryLevel
+ */
+ void log(const QString &message, QQmlJSLoggerCategory category,
+ const QQmlJS::SourceLocation &srcLocation, bool showContext = true,
+ bool showFileName = true, const std::optional<FixSuggestion> &suggestion = {},
+ const QString overrideFileName = QString())
{
- log(message, category, srcLocation, QtCriticalMsg, showContext, showFileName, suggestion);
+ log(message, category, srcLocation, m_categoryLevels[category], showContext, showFileName,
+ suggestion, overrideFileName);
}
- void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages, QtMsgType level,
+ void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
QQmlJSLoggerCategory category);
void ignoreWarnings(uint32_t line, const QSet<QQmlJSLoggerCategory> &categories)
@@ -260,12 +262,13 @@ public:
QString fileName() const { return m_fileName; }
private:
- void printContext(const QQmlJS::SourceLocation &location);
+ void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location);
void printFix(const FixSuggestion &fix);
- void log(const QString &message, QQmlJSLoggerCategory category, const QQmlJS::SourceLocation &,
- QtMsgType type, bool showContext, bool showFileName,
- const std::optional<FixSuggestion> &suggestion);
+ void log(const QString &message, QQmlJSLoggerCategory category,
+ const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName);
QString m_fileName;
QString m_code;
@@ -273,13 +276,16 @@ private:
QColorOutput m_output;
QtMsgType m_categoryLevels[QQmlJSLoggerCategory_Last + 1] = {};
- bool m_categoryError[QQmlJSLoggerCategory_Last + 1] = {};
+ bool m_categoryIgnored[QQmlJSLoggerCategory_Last + 1] = {};
bool m_categoryChanged[QQmlJSLoggerCategory_Last + 1] = {};
QList<Message> m_infos;
QList<Message> m_warnings;
QList<Message> m_errors;
QHash<uint32_t, QSet<QQmlJSLoggerCategory>> m_ignoredWarnings;
+
+ // the compiler needs private log() function at the moment
+ friend class QQmlJSAotCompiler;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp
new file mode 100644
index 0000000000..2d85343746
--- /dev/null
+++ b/src/qmlcompiler/qqmljsmetatypes.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmljsmetatypes_p.h>
+#include <private/qqmljstyperesolver_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ A binding is valid when it has both a target (m_propertyName is set)
+ and some content set (m_bindingType != Invalid).
+*/
+bool QQmlJSMetaPropertyBinding::isValid() const { return !m_propertyName.isEmpty() && bindingType() != Invalid; }
+
+QString QQmlJSMetaPropertyBinding::literalTypeName() const
+{
+ if (std::holds_alternative<Content::BoolLiteral>(m_bindingContent))
+ return QLatin1String("bool");
+ else if (std::holds_alternative<Content::NumberLiteral>(m_bindingContent))
+ return QLatin1String("double");
+ else if (std::holds_alternative<Content::StringLiteral>(m_bindingContent))
+ return QLatin1String("string");
+ else if (std::holds_alternative<Content::RegexpLiteral>(m_bindingContent))
+ return QLatin1String("regexp");
+ else if (std::holds_alternative<Content::Null>(m_bindingContent))
+ return QLatin1String("$internal$.std::nullptr_t");
+ return {};
+}
+
+bool QQmlJSMetaPropertyBinding::boolValue() const
+{
+ if (auto boolLit = std::get_if<Content::BoolLiteral>(&m_bindingContent))
+ return boolLit->value;
+ // warn
+ return false;
+}
+
+double QQmlJSMetaPropertyBinding::numberValue() const
+{
+ if (auto numberLit = std::get_if<Content::NumberLiteral>(&m_bindingContent))
+ return numberLit->value;
+ // warn
+ return 0;
+}
+
+QString QQmlJSMetaPropertyBinding::stringValue() const
+{
+ if (auto stringLiteral = std::get_if<Content::StringLiteral>(&m_bindingContent))
+ return stringLiteral->value;
+ // warn
+ return {};
+}
+
+/*!
+ \internal
+ Uses \a resolver to return the correct type for the stored literal
+ and a null scope pointer if the binding does not contain a literal
+ */
+QSharedPointer<const QQmlJSScope> QQmlJSMetaPropertyBinding::literalType(const QQmlJSTypeResolver *resolver) const
+{
+ Q_ASSERT(resolver);
+ switch (bindingType()) {
+ case QQmlJSMetaPropertyBinding::BoolLiteral:
+ return resolver->boolType();
+ case QQmlJSMetaPropertyBinding::NumberLiteral:
+ return resolver->typeForName(QLatin1String("double"));
+ case QQmlJSMetaPropertyBinding::Translation: // translations are strings
+ case QQmlJSMetaPropertyBinding::TranslationById:
+ case QQmlJSMetaPropertyBinding::StringLiteral:
+ return resolver->stringType();
+ case QQmlJSMetaPropertyBinding::RegExpLiteral:
+ return resolver->typeForName(QLatin1String("regexp"));
+ case QQmlJSMetaPropertyBinding::Null:
+ return resolver->nullType();
+ default:
+ return {};
+ }
+ Q_UNREACHABLE();
+ return {}; // needed on some compilers which do not see that every case in the switch returns
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 54627f9e35..76ec93e46b 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -59,6 +59,7 @@
QT_BEGIN_NAMESPACE
+class QQmlJSTypeResolver;
class QQmlJSScope;
class QQmlJSMetaEnum
{
@@ -390,38 +391,98 @@ public:
};
private:
- QQmlJS::SourceLocation m_sourceLocation;
- QString m_propertyName; // TODO: this is a debug-only information
- BindingType m_bindingType = BindingType::Invalid;
-
- // TODO: the below should really be put into a union (of sorts). despite the
- // data being overlapping for different things (which for now is just
- // ignored), we would still only have one kind of information stored in a
- // binding. separating the non-overlapping bits would indicate what we
- // require for each binding type more clearly
-
- //union {
- QString m_translationString;
- QString m_translationId;
- QVariant m_literalValue; // constant in literal (or null) expression
- //};
- QWeakPointer<const QQmlJSScope> m_value; // object type of Object binding *OR* a literal type
- QString m_valueTypeName;
-
- QWeakPointer<const QQmlJSScope> m_interceptor; // QQmlPropertyValueInterceptor derived type
- QString m_interceptorTypeName;
+ // needs to be kept in sync with the BindingType enum
+ struct Content {
+ using Invalid = std::monostate;
+ struct BoolLiteral {
+ bool value;
+ friend bool operator==(BoolLiteral a, BoolLiteral b) { return a.value == b.value; }
+ friend bool operator!=(BoolLiteral a, BoolLiteral b) { return !(a == b); }
+ };
+ struct NumberLiteral {
+ friend bool operator==(NumberLiteral a, NumberLiteral b) { return a.value == b.value; }
+ friend bool operator!=(NumberLiteral a, NumberLiteral b) { return !(a == b); }
+ double value; // ### TODO: int?
+ };
+ struct StringLiteral {
+ friend bool operator==(StringLiteral a, StringLiteral b) { return a.value == b.value; }
+ friend bool operator!=(StringLiteral a, StringLiteral b) { return !(a == b); }
+ QString value;
+ };
+ struct RegexpLiteral {
+ friend bool operator==(RegexpLiteral a, RegexpLiteral b) { return a.value == b.value; }
+ friend bool operator!=(RegexpLiteral a, RegexpLiteral b) { return !(a == b); }
+ QString value;
+ };
+ struct Null {
+ friend bool operator==(Null , Null ) { return true; }
+ friend bool operator!=(Null a, Null b) { return !(a == b); }
+ };
+ struct TranslationString {
+ friend bool operator==(TranslationString a, TranslationString b) { return a.value == b.value; }
+ friend bool operator!=(TranslationString a, TranslationString b) { return !(a == b); }
+ QString value;
+ };
+ struct TranslationById {
+ friend bool operator==(TranslationById a, TranslationById b) { return a.value == b.value; }
+ friend bool operator!=(TranslationById a, TranslationById b) { return !(a == b); }
+ QString value;
+ };
+ struct Script {
+ friend bool operator==(Script , Script ) { return true; }
+ friend bool operator!=(Script a, Script b) { return !(a == b); }
+ };
+ struct Object {
+ friend bool operator==(Object a, Object b) { return a.value == b.value && a.typeName == b.typeName; }
+ friend bool operator!=(Object a, Object b) { return !(a == b); }
+ QString typeName;
+ QWeakPointer<const QQmlJSScope> value;
+ };
+ struct Interceptor {
+ friend bool operator==(Interceptor a, Interceptor b)
+ {
+ return a.interceptor == b.interceptor && a.interceptorTypeName == b.interceptorTypeName;
+ }
+ friend bool operator!=(Interceptor a, Interceptor b) { return !(a == b); }
+ QString interceptorTypeName;
+ QWeakPointer<const QQmlJSScope> interceptor;
+ };
+ struct ValueSource {
+ friend bool operator==(ValueSource a, ValueSource b)
+ {
+ return a.valueSource == b.valueSource && a.valueSourceTypeName == b.valueSourceTypeName;
+ }
+ friend bool operator!=(ValueSource a, ValueSource b) { return !(a == b); }
+ QString valueSourceTypeName;
+ QWeakPointer<const QQmlJSScope> valueSource;
+ };
+ struct AttachedProperty {
+ friend bool operator==(AttachedProperty , AttachedProperty ) { return true; }
+ friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
+ };
+ struct GroupProperty {
+ friend bool operator==(GroupProperty , GroupProperty ) { return true; }
+ friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); }
+ };
+ using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
+ RegexpLiteral, Null, TranslationString,
+ TranslationById, Script, Object, Interceptor,
+ ValueSource, AttachedProperty, GroupProperty
+ >;
+ };
+ using BindingContent = Content::type;
- QWeakPointer<const QQmlJSScope> m_valueSource; // QQmlPropertyValueSource derived type
- QString m_valueSourceTypeName;
+ QQmlJS::SourceLocation m_sourceLocation;
+ QString m_propertyName; // TODO: this is a debug-only information
+ BindingContent m_bindingContent;
- void setBindingTypeOnce(BindingType type)
+ void ensureSetBindingTypeOnce()
{
- Q_ASSERT(m_bindingType == BindingType::Invalid);
- m_bindingType = type;
+ Q_ASSERT(bindingType() == BindingType::Invalid);
}
- bool isLiteralBinding() const { return isLiteralBinding(m_bindingType); }
+ bool isLiteralBinding() const { return isLiteralBinding(bindingType()); }
public:
@@ -448,90 +509,124 @@ public:
const QQmlJS::SourceLocation &sourceLocation() const { return m_sourceLocation; }
- BindingType bindingType() const { return m_bindingType; }
+ BindingType bindingType() const { return BindingType(m_bindingContent.index()); }
- bool isValid() const { return !m_propertyName.isEmpty(); }
+ bool isValid() const;
+
+ void setStringLiteral(QAnyStringView value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::StringLiteral { value.toString() };
+ }
+
+ void setScriptBinding()
+ {
+ // ### TODO: this does not allow us to do anything interesting with the binding
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Script {};
+ }
- void setLiteral(BindingType kind, const QString &typeName, const QVariant &value,
- const QSharedPointer<const QQmlJSScope> &type)
+ void setBoolLiteral(bool value)
{
- Q_ASSERT(isLiteralBinding(kind));
- setBindingTypeOnce(kind);
- m_value = type;
- m_valueTypeName = typeName;
- m_literalValue = value;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::BoolLiteral { value };
+ }
+
+ void setNullLiteral()
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Null {};
+ }
+
+ void setNumberLiteral(double value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::NumberLiteral { value };
+ }
+
+ void setRegexpLiteral(QAnyStringView value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::RegexpLiteral { value.toString() };
}
// ### TODO: we might need comment and translation number at some point
void setTranslation(QStringView translation)
{
- setBindingTypeOnce(BindingType::Translation);
- m_translationString = translation.toString();
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::TranslationString { translation.toString() };
}
void setTranslationId(QStringView id)
{
- setBindingTypeOnce(BindingType::TranslationById);
- m_translationId = id.toString();
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::TranslationById { id.toString() };
}
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::Object);
- m_value = type;
- m_valueTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Object { typeName, type };
}
void setInterceptor(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::Interceptor);
- m_interceptor = type;
- m_interceptorTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Interceptor { typeName, type };
}
void setValueSource(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::ValueSource);
- m_valueSource = type;
- m_valueSourceTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::ValueSource { typeName, type };
}
- QString literalTypeName() const { return m_valueTypeName; }
- const QVariant &literalValue() const { return m_literalValue; }
- QSharedPointer<const QQmlJSScope> literalType() const { return m_value; }
+ QString literalTypeName() const;
+
+ // ### TODO: here and below: Introduce an allowConversion parameter, if yes, enable conversions e.g. bool -> number?
+ bool boolValue() const;
- QString objectTypeName() const { return m_valueTypeName; }
- QSharedPointer<const QQmlJSScope> objectType() const { return m_value; }
+ double numberValue() const;
- QString interceptorTypeName() const { return m_interceptorTypeName; }
- QSharedPointer<const QQmlJSScope> interceptorType() const { return m_interceptor; }
+ QString stringValue() const;
- QString valueSourceTypeName() const { return m_valueSourceTypeName; }
- QSharedPointer<const QQmlJSScope> valueSourceType() const { return m_valueSource; }
+ QSharedPointer<const QQmlJSScope> literalType(const QQmlJSTypeResolver *resolver) const;
+
+ QString objectTypeName() const
+ {
+ if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
+ return object->typeName;
+ // warn
+ return {};
+ }
+ QSharedPointer<const QQmlJSScope> objectType() const
+ {
+ if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
+ return object->value.lock();
+ // warn
+ return {};
+ }
bool hasLiteral() const
{
- return isLiteralBinding()
- && (!m_literalValue.isNull() || m_bindingType == QQmlJSMetaPropertyBinding::Null);
+ // TODO: Assumption: if the type is literal, we must have one
+ return isLiteralBinding();
}
- bool hasObject() const { return m_bindingType == BindingType::Object && !m_value.isNull(); }
+ bool hasObject() const { return bindingType() == BindingType::Object; }
bool hasInterceptor() const
{
- return m_bindingType == BindingType::Interceptor && !m_interceptor.isNull();
+ return bindingType() == BindingType::Interceptor;
}
bool hasValueSource() const
{
- return m_bindingType == BindingType::ValueSource && !m_valueSource.isNull();
+ return bindingType() == BindingType::ValueSource;
}
friend bool operator==(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
{
- return a.m_bindingType == b.m_bindingType && a.m_propertyName == b.m_propertyName
- && a.m_valueTypeName == b.m_valueTypeName
- && a.m_interceptorTypeName == b.m_interceptorTypeName
- && a.m_valueSourceTypeName == b.m_valueSourceTypeName
- && a.m_literalValue == b.m_literalValue && a.m_value == b.m_value
- && a.m_interceptor == b.m_interceptor && a.m_sourceLocation == b.m_sourceLocation;
+ return a.m_propertyName == b.m_propertyName
+ && a.m_bindingContent == b.m_bindingContent
+ && a.m_sourceLocation == b.m_sourceLocation;
}
friend bool operator!=(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
@@ -541,12 +636,9 @@ public:
friend size_t qHash(const QQmlJSMetaPropertyBinding &binding, size_t seed = 0)
{
- return qHashMulti(seed, binding.m_propertyName, binding.m_valueTypeName,
- binding.m_interceptorTypeName, binding.m_valueSourceTypeName,
- binding.m_literalValue.toString(), binding.m_value.toStrongRef().data(),
- binding.m_interceptor.toStrongRef().data(),
- binding.m_valueSource.toStrongRef().data(), binding.m_sourceLocation,
- binding.m_bindingType);
+ // we don't need to care about the actual binding content when hashing
+ return qHashMulti(seed, binding.m_propertyName, binding.m_sourceLocation,
+ binding.bindingType());
}
};
diff --git a/src/qmlcompiler/qqmljsregistercontent.cpp b/src/qmlcompiler/qqmljsregistercontent.cpp
index aef0453eac..57d101cf16 100644
--- a/src/qmlcompiler/qqmljsregistercontent.cpp
+++ b/src/qmlcompiler/qqmljsregistercontent.cpp
@@ -36,9 +36,9 @@ QString QQmlJSRegisterContent::descriptiveName() const
QString result = m_storedType->internalName() + u" of "_qs;
const auto scope = [this]() -> QString {
return (m_scope->internalName().isEmpty()
- ? (m_scope->fileName().isEmpty()
+ ? (m_scope->filePath().isEmpty()
? u"??"_qs
- : (u"(component in "_qs + m_scope->fileName() + u")"_qs))
+ : (u"(component in "_qs + m_scope->filePath() + u")"_qs))
: m_scope->internalName())
+ u"::"_qs;
};
@@ -67,6 +67,10 @@ QString QQmlJSRegisterContent::descriptiveName() const
case ImportNamespace: {
return u"import namespace %1"_qs.arg(std::get<uint>(m_content));
}
+ case Conversion: {
+ return u"conversion to %1"_qs.arg(
+ std::get<ConvertedTypes>(m_content).result->internalName());
+ }
}
Q_UNREACHABLE();
return result + u"wat?"_qs;
@@ -83,6 +87,9 @@ bool QQmlJSRegisterContent::isList() const
return prop.isList()
|| prop.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
}
+ case Conversion:
+ return std::get<ConvertedTypes>(m_content).result->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Sequence;
default:
return false;
}
@@ -153,6 +160,17 @@ QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr
return result;
}
+QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr &storedType,
+ const QList<QQmlJSScope::ConstPtr> origins,
+ const QQmlJSScope::ConstPtr &conversion,
+ ContentVariant variant,
+ const QQmlJSScope::ConstPtr &scope)
+{
+ QQmlJSRegisterContent result(storedType, scope, variant);
+ result.m_content = ConvertedTypes { origins, conversion };
+ return result;
+}
+
bool operator==(const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
{
return a.m_storedType == b.m_storedType && a.m_variant == b.m_variant && a.m_scope == b.m_scope
diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h
index 174eeeb3bc..690ee5da4d 100644
--- a/src/qmlcompiler/qqmljsregistercontent_p.h
+++ b/src/qmlcompiler/qqmljsregistercontent_p.h
@@ -77,6 +77,9 @@ public:
ExtensionObjectMethod,
ExtensionObjectEnum,
+ MethodReturnValue,
+ JavaScriptReturnValue,
+
ListValue,
Builtin,
Unknown,
@@ -98,6 +101,7 @@ public:
bool isEnumeration() const { return m_content.index() == Enum; }
bool isMethod() const { return m_content.index() == Method; }
bool isImportNamespace() const { return m_content.index() == ImportNamespace; }
+ bool isConversion() const { return m_content.index() == Conversion; }
bool isList() const;
bool isWritable() const;
@@ -118,6 +122,16 @@ public:
QList<QQmlJSMetaMethod> method() const { return std::get<QList<QQmlJSMetaMethod>>(m_content); }
uint importNamespace() const { return std::get<uint>(m_content); }
+ QQmlJSScope::ConstPtr conversionResult() const
+ {
+ return std::get<ConvertedTypes>(m_content).result;
+ }
+
+ QList<QQmlJSScope::ConstPtr> conversionOrigins() const
+ {
+ return std::get<ConvertedTypes>(m_content).origins;
+ }
+
ContentVariant variant() const { return m_variant; }
friend size_t qHash(const QQmlJSRegisterContent &registerContent, size_t seed = 0)
@@ -136,6 +150,8 @@ public:
return qHash(std::get<QList<QQmlJSMetaMethod>>(registerContent.m_content), seed);
case ImportNamespace:
return qHash(std::get<uint>(registerContent.m_content), seed);
+ case Conversion:
+ return qHash(std::get<ConvertedTypes>(registerContent.m_content), seed);
}
Q_UNREACHABLE();
@@ -164,6 +180,12 @@ public:
uint importNamespaceStringId, ContentVariant variant,
const QQmlJSScope::ConstPtr &scope = {});
+ static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
+ const QList<QQmlJSScope::ConstPtr> origins,
+ const QQmlJSScope::ConstPtr &conversion,
+ ContentVariant variant,
+ const QQmlJSScope::ConstPtr &scope = {});
+
QQmlJSRegisterContent storedIn(const QQmlJSScope::ConstPtr &newStoredType) const
{
QQmlJSRegisterContent result = *this;
@@ -172,14 +194,36 @@ public:
}
private:
- enum ContentKind { Type, Property, Enum, Method, ImportNamespace };
+ enum ContentKind { Type, Property, Enum, Method, ImportNamespace, Conversion };
+
+ struct ConvertedTypes
+ {
+ QList<QQmlJSScope::ConstPtr> origins;
+ QQmlJSScope::ConstPtr result;
+
+ friend size_t qHash(const ConvertedTypes &types, size_t seed = 0)
+ {
+ return qHashMulti(seed, types.origins, types.result);
+ }
+
+ friend bool operator==(const ConvertedTypes &a, const ConvertedTypes &b)
+ {
+ return a.origins == b.origins && a.result == b.result;
+ }
+
+ friend bool operator!=(const ConvertedTypes &a, const ConvertedTypes &b)
+ {
+ return !(a == b);
+ }
+ };
using Content = std::variant<
QQmlJSScope::ConstPtr,
QQmlJSMetaProperty,
std::pair<QQmlJSMetaEnum, QString>,
QList<QQmlJSMetaMethod>,
- uint
+ uint,
+ ConvertedTypes
>;
QQmlJSRegisterContent(const QQmlJSScope::ConstPtr &storedType,
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index c01c5d9471..24f1ba6687 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -103,6 +103,15 @@ QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &par
return childScope;
}
+QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
+{
+ if (origin.isNull())
+ return QQmlJSScope::Ptr();
+ QQmlJSScope::Ptr cloned = create(origin->m_scopeType, origin->m_parentScope);
+ *cloned = *origin;
+ return cloned;
+}
+
void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
{
Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope);
@@ -437,11 +446,11 @@ void QQmlJSScope::resolveNonEnumTypes(
void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType)
{
- Q_ASSERT(intType); // There always has to be a builtin "int" type
for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end;
++it) {
if (it->type())
continue;
+ Q_ASSERT(intType); // We need an "int" type to resolve enums
auto enumScope = QQmlJSScope::create(EnumScope, self);
enumScope->m_baseTypeName = QStringLiteral("int");
enumScope->m_baseType.scope = intType;
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 2cc6555c30..72c57be7d6 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -59,7 +59,6 @@ class QQmlJSImporter;
class QQmlJSScope
{
- Q_DISABLE_COPY(QQmlJSScope)
public:
QQmlJSScope(QQmlJSScope &&) = default;
QQmlJSScope &operator=(QQmlJSScope &&) = default;
@@ -181,7 +180,8 @@ public:
};
static QQmlJSScope::Ptr create(ScopeType type = QQmlJSScope::QMLScope,
- const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin);
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
QQmlJSScope::Ptr parentScope()
@@ -224,8 +224,8 @@ public:
void setAnnotations(const QList<QQmlJSAnnotation> &annotation) { m_annotations = std::move(annotation); }
const QList<QQmlJSAnnotation> &annotations() const { return m_annotations; }
- QString fileName() const { return m_fileName; }
- void setFileName(const QString &file) { m_fileName = file; }
+ QString filePath() const { return m_filePath; }
+ void setFilePath(const QString &file) { m_filePath = file; }
// The name the type uses to refer to itself. Either C++ class name or base name of
// QML file. isComposite tells us if this is a C++ or a QML name.
@@ -462,6 +462,8 @@ public:
private:
QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ QQmlJSScope(const QQmlJSScope &) = default;
+ QQmlJSScope &operator=(const QQmlJSScope &) = default;
static ImportedScope<QQmlJSScope::ConstPtr> findType(
const QString &name, const ContextualTypes &contextualTypes,
@@ -484,7 +486,7 @@ private:
QVector<QQmlJSScope::Ptr> m_childScopes;
QQmlJSScope::WeakPtr m_parentScope;
- QString m_fileName;
+ QString m_filePath;
QString m_internalName;
QString m_baseTypeName;
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index e1b98deda0..283553f758 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -58,18 +58,22 @@ void QQmlJSShadowCheck::run(
m_annotations = annotations;
m_function = function;
m_error = error;
- m_state = initialState(function, m_typeResolver);
+ m_state = initialState(function);
decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
}
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->stringForIndex(nameIndex));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->stringForIndex(nameIndex));
}
void QQmlJSShadowCheck::generate_GetLookup(int index)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->lookupName(index));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->lookupName(index));
}
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
@@ -122,7 +126,7 @@ void QQmlJSShadowCheck::checkShadowing(
}
setError(u"Member %1 of %2 can be shadowed"_qs
- .arg(memberName, m_state.accumulatorIn.descriptiveName()));
+ .arg(memberName, m_state.accumulatorIn().descriptiveName()));
return;
}
default:
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 4512d596e1..ce07a333d5 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -61,39 +61,25 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
}
}
- for (QQmlJSScope::ConstPtr &argument : function->argumentTypes) {
- Q_ASSERT(argument);
- if (QQmlJSScope::ConstPtr stored = m_typeResolver->genericType(
- argument, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- argument = std::move(stored);
- } else {
- setError(QStringLiteral("Cannot store the argument type %1.")
- .arg(argument->internalName(), 0));
- return InstructionAnnotations();
- }
- }
+ const auto transformRegister = [&](QQmlJSRegisterContent &content) {
+ if (const QQmlJSScope::ConstPtr &specific = content.storedType())
+ m_typeResolver->generalizeType(specific);
+ };
- const auto transformRegisters = [&](QHash<int, QQmlJSRegisterContent> &registers, int offset) {
- for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j) {
- const QQmlJSRegisterContent &content = *j;
- if (QQmlJSScope::ConstPtr specific = content.storedType()) {
- if (QQmlJSScope::ConstPtr generic = m_typeResolver->genericType(specific)) {
- *j = content.storedIn(generic);
- } else {
- setError(QStringLiteral("Cannot store the register type %1.")
- .arg(specific->internalName()), offset);
- return false;
- }
- }
- }
- return true;
+ const auto transformRegisters
+ = [&](QFlatMap<int, QQmlJSRegisterContent> &registers) {
+ for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j)
+ transformRegister(j.value());
};
+ for (QQmlJSRegisterContent &argument : function->argumentTypes) {
+ Q_ASSERT(argument.isValid());
+ transformRegister(argument);
+ }
+
for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
- if (!transformRegisters(i->registers, i.key()))
- return InstructionAnnotations();
- if (!transformRegisters(i->expectedTargetTypesBeforeJump, i.key()))
- return InstructionAnnotations();
+ transformRegister(i->second.changedRegister);
+ transformRegisters(i->second.typeConversions);
}
return annotations;
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 63a552e293..440cf2da29 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -213,7 +213,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
} else if (script) {
QString name = toString(script->qualifiedId);
if (name == QLatin1String("file")) {
- scope->setFileName(readStringBinding(script));
+ scope->setFilePath(readStringBinding(script));
} else if (name == QLatin1String("name")) {
scope->setInternalName(readStringBinding(script));
} else if (name == QLatin1String("prototype")) {
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 6072393e0f..2760f3401b 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -60,8 +60,13 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_returnType = m_typeResolver->globalType(m_function->returnType);
do {
+ // Reset the error if we need to do another pass
+ if (m_state.needsMorePasses)
+ *m_error = QQmlJS::DiagnosticMessage();
+
+ m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
- m_state.State::operator=(initialState(m_function, m_typeResolver));
+ m_state.State::operator=(initialState(m_function));
reset();
decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
@@ -69,7 +74,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
// If we have found unresolved backwards jumps, we need to start over with a fresh state.
// Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case.
// This means that we won't start over for the same reason again.
- } while (!m_error->isValid() && m_state.needsMorePasses);
+ } while (m_state.needsMorePasses);
return m_state.annotations;
}
@@ -80,34 +85,41 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
return;
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
- m_logger->logWarning( \
- u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
- Log_Compiler); \
+ m_logger->log(u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
+ Log_Compiler, QQmlJS::SourceLocation()); \
return;
void QQmlJSTypePropagator::generate_Ret()
{
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
- } else if (!m_returnType.isValid() && m_state.accumulatorIn.isValid()
- && m_typeResolver->containedType(m_state.accumulatorIn)
- != m_typeResolver->voidType()) {
+ } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
+ && !m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->voidType())) {
setError(u"function without type annotation returns %1"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
- } else if (m_state.accumulatorIn != m_returnType
- && !canConvertFromTo(m_state.accumulatorIn, m_returnType)) {
+ } else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(),
+ .arg(m_state.accumulatorIn().descriptiveName(),
m_returnType.descriptiveName()));
- m_logger->logWarning(u"Cannot assign binding of type %1 to %2"_qs.arg(
- m_typeResolver->containedTypeName(m_state.accumulatorIn),
- m_typeResolver->containedTypeName(m_returnType)),
- Log_Type, getCurrentBindingSourceLocation());
+ m_logger->log(u"Cannot assign binding of type %1 to %2"_qs.arg(
+ m_typeResolver->containedTypeName(m_state.accumulatorIn()),
+ m_typeResolver->containedTypeName(m_returnType)),
+ Log_Type, getCurrentBindingSourceLocation());
return;
}
+ if (m_returnType.isValid()) {
+ // We need to preserve any possible undefined value as that resets the property.
+ if (m_typeResolver->canHoldUndefined(m_state.accumulatorIn()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_returnType);
+ }
+
+ m_state.setHasSideEffects(true);
m_state.skipInstructionsUntilNextJumpTarget = true;
}
@@ -119,37 +131,37 @@ void QQmlJSTypePropagator::generate_Debug()
void QQmlJSTypePropagator::generate_LoadConst(int index)
{
auto encodedConst = m_jsUnitGenerator->constant(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadZero()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadFalse()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->nullType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
}
void QQmlJSTypePropagator::generate_LoadUndefined()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->voidType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
}
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
@@ -160,17 +172,24 @@ void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
void QQmlJSTypePropagator::generate_LoadReg(int reg)
{
- m_state.accumulatorOut = checkedInputRegister(reg);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(Accumulator, checkedInputRegister(reg));
}
void QQmlJSTypePropagator::generate_StoreReg(int reg)
{
- setRegister(reg, m_state.accumulatorIn);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(reg, m_state.accumulatorIn());
}
void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
{
- setRegister(destReg, m_state.registers[srcReg]);
+ Q_ASSERT(destReg != InvalidRegister);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(destReg, m_state.registers[srcReg]);
}
void QQmlJSTypePropagator::generate_LoadImport(int index)
@@ -182,7 +201,7 @@ void QQmlJSTypePropagator::generate_LoadImport(int index)
void QQmlJSTypePropagator::generate_LoadLocal(int index)
{
Q_UNUSED(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_StoreLocal(int index)
@@ -208,7 +227,7 @@ void QQmlJSTypePropagator::generate_StoreScopedLocal(int scope, int index)
void QQmlJSTypePropagator::generate_LoadRuntimeString(int stringId)
{
Q_UNUSED(stringId)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
// m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
}
@@ -228,8 +247,8 @@ void QQmlJSTypePropagator::generate_LoadClosure(int value)
void QQmlJSTypePropagator::generate_LoadName(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(m_function->qmlScope, name);
- if (!m_state.accumulatorOut.isValid())
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -260,7 +279,7 @@ QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() c
return combine(entries.constFirst().location, entries.constLast().location);
}
-void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
+void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isMethod) const
{
auto location = getCurrentSourceLocation();
@@ -271,6 +290,13 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
return;
}
+ if (isMethod) {
+ if (isCallingProperty(m_function->qmlScope, name))
+ return;
+ } else if (isMissingPropertyType(m_function->qmlScope, name)) {
+ return;
+ }
+
std::optional<FixSuggestion> suggestion;
auto childScopes = m_function->qmlScope->childScopes();
@@ -297,7 +323,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
const auto handler = m_typeResolver->signalHandlers()[id.location];
- QString fixString = handler.isMultiline ? u" function("_qs : u" ("_qs;
+ QString fixString = handler.isMultiline ? u"function("_qs : u"("_qs;
const auto parameters = handler.signalParameters;
for (int numParams = parameters.size(); numParams > 0; --numParams) {
fixString += parameters.at(parameters.size() - numParams);
@@ -314,7 +340,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
"function instead.\n")
.arg(id.location.startLine)
.arg(id.location.startColumn),
- fixLocation, fixString
+ fixLocation, fixString, QString(), false
};
}
break;
@@ -334,7 +360,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
name + QLatin1String(" is a member of a parent element\n")
+ QLatin1String(" You can qualify the access with its id "
"to avoid this warning:\n"),
- fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.'))
+ fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.')), QString(), id.isEmpty()
};
if (id.isEmpty()) {
@@ -358,8 +384,8 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
}
}
- m_logger->logWarning(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true,
- true, suggestion);
+ m_logger->log(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true, true,
+ suggestion);
}
void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name,
@@ -407,65 +433,122 @@ void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QS
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, getCurrentSourceLocation());
+ m_logger->log(message, Log_Deprecation, getCurrentSourceLocation());
}
-bool QQmlJSTypePropagator::checkRestricted(const QString &propertyName) const
+bool QQmlJSTypePropagator::isRestricted(const QString &propertyName) const
{
QString restrictedKind;
- if (!m_state.accumulatorIn.isValid())
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn == m_state.registers.end())
return false;
- if (m_state.accumulatorIn.isList() && propertyName != u"length") {
+ if (accumulatorIn.value().isList() && propertyName != u"length") {
restrictedKind = u"a list"_qs;
- } else if (m_state.accumulatorIn.isEnumeration()
- && !m_state.accumulatorIn.enumeration().hasKey(propertyName)) {
+ } else if (accumulatorIn.value().isEnumeration()
+ && !accumulatorIn.value().enumeration().hasKey(propertyName)) {
restrictedKind = u"an enum"_qs;
- } else if (m_state.accumulatorIn.isMethod()) {
+ } else if (accumulatorIn.value().isMethod()) {
restrictedKind = u"a method"_qs;
}
if (!restrictedKind.isEmpty())
- m_logger->logWarning(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(
- restrictedKind, propertyName),
- Log_Type, getCurrentSourceLocation());
+ m_logger->log(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(restrictedKind,
+ propertyName),
+ Log_Type, getCurrentSourceLocation());
return !restrictedKind.isEmpty();
}
+// Only to be called once a lookup has already failed
+bool QQmlJSTypePropagator::isMissingPropertyType(QQmlJSScope::ConstPtr scope,
+ const QString &propertyName) const
+{
+ auto property = scope->property(propertyName);
+ if (!property.isValid())
+ return false;
+
+ QString errorType;
+ if (property.type().isNull())
+ errorType = u"found"_qs;
+ else if (!property.type()->isFullyResolved())
+ errorType = u"fully resolved"_qs;
+
+ Q_ASSERT(!errorType.isEmpty());
+
+ m_logger->log(
+ u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
+ .arg(property.typeName(), propertyName, errorType),
+ Log_Type, getCurrentSourceLocation());
+
+ return true;
+}
+
+bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const
+{
+ auto property = scope->property(name);
+ if (!property.isValid())
+ return false;
+
+ QString propertyType = u"Property"_qs;
+
+ auto methods = scope->methods(name);
+
+ QString errorType;
+ if (!methods.isEmpty()) {
+ errorType = u"shadowed by a property."_qs;
+ switch (methods.first().methodType()) {
+ case QQmlJSMetaMethod::Signal:
+ propertyType = u"Signal"_qs;
+ break;
+ case QQmlJSMetaMethod::Slot:
+ propertyType = u"Slot"_qs;
+ break;
+ case QQmlJSMetaMethod::Method:
+ propertyType = u"Method"_qs;
+ break;
+ }
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
+ errorType =
+ u"a variant property. It may or may not be a method. Use a regular function instead."_qs;
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->jsValueType())) {
+ errorType =
+ u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_qs;
+ } else {
+ errorType = u"not a method"_qs;
+ }
+
+ m_logger->log(u"%1 \"%2\" is %3"_qs.arg(propertyType, name, errorType), Log_Type,
+ getCurrentSourceLocation(), true, true, {});
+
+ return true;
+}
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
+ // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
+ // Any import namespaces etc. are handled via LoadProperty or GetLookup.
+
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(
- m_function->qmlScope,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
- + u'.' + name
- : name);
-
- if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
- const QQmlJSRegisterContent inType = m_state.accumulatorIn.isValid()
- ? m_state.accumulatorIn
- : m_typeResolver->globalType(m_function->qmlScope);
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
- m_typeResolver->containedType(inType));
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+
+ if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
+ const QQmlJSRegisterContent inType = m_typeResolver->globalType(m_function->qmlScope);
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_typeResolver->voidType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
+ m_typeResolver->containedType(inType)));
return;
}
checkDeprecated(m_function->qmlScope, name, false);
- bool isRestricted = checkRestricted(name);
-
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot access value for name "_qs + name);
-
- if (!isRestricted)
- handleUnqualifiedAccess(name);
- } else if (m_typeResolver->genericType(m_state.accumulatorOut.storedType()).isNull()) {
+ handleUnqualifiedAccess(name, false);
+ } else if (m_typeResolver->genericType(m_state.accumulatorOut().storedType()).isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
setError(u"Cannot determine generic type for "_qs + name);
@@ -490,20 +573,24 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
if (!type.isWritable() && !m_function->qmlScope->hasOwnProperty(name)) {
setError(u"Can't assign to read-only property %1"_qs.arg(name));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
- getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, type)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), type)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), type.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), type.descriptiveName()));
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
@@ -511,41 +598,76 @@ void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
void QQmlJSTypePropagator::generate_LoadElement(int base)
{
const QQmlJSRegisterContent baseRegister = m_state.registers[base];
- if (!m_typeResolver->registerContains(m_state.accumulatorIn, m_typeResolver->intType())
- || baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ setAccumulator(jsValue);
return;
}
- m_state.accumulatorOut = m_typeResolver->valueType(baseRegister);
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->intType()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ setAccumulator(m_typeResolver->valueType(baseRegister));
}
void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
{
- Q_UNUSED(base)
- Q_UNUSED(index)
+ const QQmlJSRegisterContent baseRegister = m_state.registers[base];
+ const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(indexRegister)) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ addReadRegister(index, jsValue);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(indexRegister, m_typeResolver->intType()))
+ addReadRegister(index, indexRegister);
+ else
+ addReadRegister(index, m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ addReadAccumulator(m_typeResolver->valueType(baseRegister));
+
+ // If we're writing a QQmlListProperty backed by a container somewhere else,
+ // that has side effects.
+ // If we're writing to a list retrieved from a property, that _should_ have side effects,
+ // but currently the QML engine doesn't implement them.
+ // TODO: Figure out the above and accurately set the flag.
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{
- m_state.accumulatorOut =
+ setAccumulator(
m_typeResolver->memberType(
- m_state.accumulatorIn,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
+ m_state.accumulatorIn(),
+ m_state.accumulatorIn().isImportNamespace()
+ ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
+ u'.' + propertyName
- : propertyName);
+ : propertyName));
if (m_typeInfo != nullptr
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ScopeAttached) {
- QQmlJSScope::ConstPtr attachedType = m_state.accumulatorIn.scopeType();
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ScopeAttached) {
+ QQmlJSScope::ConstPtr attachedType = m_typeResolver->originalType(
+ m_state.accumulatorIn().scopeType());
for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
scope = scope->parentScope()) {
if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
// Ignore enum accesses, as these will not cause the attached object to be created
- if (m_state.accumulatorOut.isValid() && m_state.accumulatorOut.isEnumeration())
+ if (m_state.accumulatorOut().isValid() && m_state.accumulatorOut().isEnumeration())
continue;
const QString id = m_function->addressableScopes.id(scope);
@@ -555,11 +677,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
fixLocation.length = 0;
- suggestion.fixes << FixSuggestion::Fix {
- u"Reference it by id instead:"_qs,
- fixLocation,
- id.isEmpty() ? u"<id>."_qs : (id + u'.')
- };
+ suggestion.fixes << FixSuggestion::Fix { u"Reference it by id instead:"_qs,
+ fixLocation,
+ id.isEmpty() ? u"<id>."_qs : (id + u'.'),
+ QString(), id.isEmpty() };
fixLocation = scope->sourceLocation();
fixLocation.length = 0;
@@ -571,9 +692,9 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{} };
}
- m_logger->logWarning(
+ m_logger->log(
u"Using attached type %1 already initialized in a parent scope."_qs.arg(
- m_state.accumulatorIn.scopeType()->internalName()),
+ m_state.accumulatorIn().scopeType()->internalName()),
Log_AttachedPropertyReuse, getCurrentSourceLocation(), true, true,
suggestion);
}
@@ -581,61 +702,47 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
}
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
- Q_ASSERT(m_state.accumulatorIn.isValid());
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- m_state.accumulatorIn.storedType(),
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ addReadAccumulator(m_state.accumulatorIn());
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_state.accumulatorIn().storedType(),
m_jsUnitGenerator->getStringId(propertyName),
QQmlJSRegisterContent::ObjectModulePrefix,
- m_typeResolver->containedType(m_state.accumulatorIn));
+ m_typeResolver->containedType(m_state.accumulatorIn())));
return;
}
- if (m_state.accumulatorIn.isImportNamespace())
- m_logger->logWarning(u"Type not found in namespace"_qs, Log_Type,
- getCurrentSourceLocation());
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
- m_logger->logWarning(u"Cannot load singleton as property of object"_qs, Log_Type,
- getCurrentSourceLocation());
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ if (m_state.accumulatorIn().isImportNamespace())
+ m_logger->log(u"Type not found in namespace"_qs, Log_Type, getCurrentSourceLocation());
+ } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
+ m_logger->log(u"Cannot load singleton as property of object"_qs, Log_Type,
+ getCurrentSourceLocation());
+ setAccumulator(QQmlJSRegisterContent());
}
- bool isRestricted = checkRestricted(propertyName);
+ const bool isRestrictedProperty = isRestricted(propertyName);
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot load property %1 from %2."_qs
- .arg(propertyName, m_state.accumulatorIn.descriptiveName()));
+ .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
- if (isRestricted)
+ if (isRestrictedProperty)
return;
- const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn);
+ const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn());
if (typeName == u"QVariant")
return;
- if (m_state.accumulatorIn.isList() && propertyName == u"length")
+ if (m_state.accumulatorIn().isList() && propertyName == u"length")
return;
- auto baseType = m_typeResolver->containedType(m_state.accumulatorIn);
+ auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
// Warn separately when a property is only not found because of a missing type
- if (auto property = baseType->property(propertyName); property.isValid()) {
-
- QString errorType;
- if (property.type().isNull())
- errorType = u"found"_qs;
- else if (!property.type()->isFullyResolved())
- errorType = u"fully resolved"_qs;
-
- Q_ASSERT(!errorType.isEmpty());
-
- m_logger->logWarning(
- u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
- .arg(property.typeName(), propertyName, errorType),
- Log_Type, getCurrentSourceLocation());
+ if (isMissingPropertyType(baseType, propertyName))
return;
- }
std::optional<FixSuggestion> fixSuggestion;
@@ -650,30 +757,45 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
}
- m_logger->logWarning(
+ m_logger->log(
u"Property \"%1\" not found on type \"%2\""_qs.arg(propertyName).arg(typeName),
Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- if (m_state.accumulatorOut.isMethod() && m_state.accumulatorOut.method().length() != 1) {
+ if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().length() != 1) {
setError(u"Cannot determine overloaded method on loadProperty"_qs);
return;
}
- if (m_state.accumulatorOut.isProperty()) {
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType())) {
+ if (m_state.accumulatorOut().isProperty()) {
+ if (m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
setError(u"Type %1 does not have a property %2 for reading"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), propertyName));
+ .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
return;
}
- if (!m_state.accumulatorOut.property().type()) {
- m_logger->logWarning(
- QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
- Log_Type, getCurrentSourceLocation());
+ if (!m_state.accumulatorOut().property().type()) {
+ m_logger->log(
+ QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
+ Log_Type, getCurrentSourceLocation());
}
}
+
+ switch (m_state.accumulatorOut().variant()) {
+ case QQmlJSRegisterContent::ObjectEnum:
+ case QQmlJSRegisterContent::ExtensionObjectEnum:
+ case QQmlJSRegisterContent::Singleton:
+ // For reading enums or singletons, we don't need to access anything, unless it's an
+ // import namespace. Then we need the name.
+ if (m_state.accumulatorIn().isImportNamespace())
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ default:
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ }
}
void QQmlJSTypePropagator::generate_LoadProperty(int nameIndex)
@@ -715,17 +837,21 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
if (!property.isWritable()) {
setError(u"Can't assign to read-only property %1"_qs.arg(propertyName));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(propertyName),
- Log_Property, getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(propertyName), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, property)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), property.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
return;
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(property);
+ addReadRegister(base, callBase);
}
void QQmlJSTypePropagator::generate_SetLookup(int index, int base)
@@ -762,6 +888,7 @@ void QQmlJSTypePropagator::generate_Resume(int)
void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -770,6 +897,7 @@ void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -779,22 +907,47 @@ void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, i
void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
{
- auto callBase = m_state.registers[base];
+ Q_ASSERT(m_state.registers.contains(base));
+ const auto callBase = m_state.registers[base];
const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
- const auto member = m_typeResolver->memberType(callBase, propertyName);
- const auto containedType = m_typeResolver->containedType(callBase);
- if (containedType == m_typeResolver->jsValueType()
- || containedType == m_typeResolver->varType()) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ if (m_typeResolver->registerContains(
+ callBase, m_typeResolver->jsGlobalObject()->property(u"Math"_qs).type())) {
+
+ // If we call a method on the Math object we don't need the actual Math object. We do need
+ // to transfer the type information to the code generator so that it knows that this is the
+ // Math object. Read the base register as void. void isn't stored, and the place where it's
+ // created will be optimized out if there are no other readers. The code generator can
+ // retrieve the original type and determine that it was the Math object.
+ addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
+
+ QQmlJSRegisterContent realType = m_typeResolver->globalType(m_typeResolver->realType());
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, realType);
+ setAccumulator(realType);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
+ || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
+ const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(base, jsValueType);
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, jsValueType);
+ setAccumulator(jsValueType);
+ m_state.setHasSideEffects(true);
return;
}
+ const auto member = m_typeResolver->memberType(callBase, propertyName);
if (!member.isMethod()) {
setError(u"Type %1 does not have a property %2 for calling"_qs
.arg(callBase.descriptiveName(), propertyName));
- if (checkRestricted(propertyName))
+ if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
+ return;
+
+ if (isRestricted(propertyName))
return;
std::optional<FixSuggestion> fixSuggestion;
@@ -809,14 +962,15 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
}
}
- m_logger->logWarning(u"Property \"%1\" not found on type \"%2\""_qs.arg(
- propertyName, m_typeResolver->containedTypeName(callBase)),
- Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
+ m_logger->log(u"Property \"%1\" not found on type \"%2\""_qs.arg(
+ propertyName, m_typeResolver->containedTypeName(callBase)),
+ Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- checkDeprecated(containedType, propertyName, true);
+ checkDeprecated(m_typeResolver->containedType(callBase), propertyName, true);
+ addReadRegister(base, callBase);
propagateCall(member.method(), argc, argv);
}
@@ -873,6 +1027,72 @@ QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMe
return javascriptFunction;
}
+void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
+{
+ setRegister(Accumulator, content);
+}
+
+void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
+{
+ // If we've come to the same conclusion before, let's not track the type again.
+ auto it = m_prevStateAnnotations.find(currentInstructionOffset());
+ if (it != m_prevStateAnnotations.end()) {
+ const QQmlJSRegisterContent &lastTry = it->second.changedRegister;
+ if (m_typeResolver->registerContains(lastTry, m_typeResolver->containedType(content))) {
+ m_state.setRegister(index, lastTry);
+ return;
+ }
+ }
+
+ m_state.setRegister(index, m_typeResolver->tracked(content));
+}
+
+void QQmlJSTypePropagator::mergeRegister(
+ int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
+{
+ auto merged = m_typeResolver->merge(a, b);
+
+ Q_ASSERT(merged.isValid());
+ Q_ASSERT(merged.isConversion());
+
+ auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
+ auto it = m_prevStateAnnotations.find(currentInstructionOffset());
+ if (it == m_prevStateAnnotations.end())
+ return false;
+
+ auto conversion = it->second.typeConversions.find(index);
+ if (conversion == it->second.typeConversions.end())
+ return false;
+
+ const QQmlJSRegisterContent &lastTry = conversion.value();
+
+ Q_ASSERT(lastTry.isValid());
+ Q_ASSERT(lastTry.isConversion());
+
+ if (!m_typeResolver->equals(lastTry.conversionResult(), merged.conversionResult())
+ || lastTry.conversionOrigins() != merged.conversionOrigins()) {
+ return false;
+ }
+
+ // We don't need to track it again if we've come to the same conclusion before.
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
+ m_state.registers[index] = lastTry;
+ return true;
+ };
+
+ if (!tryPrevStateConversion(index, merged)) {
+ merged = m_typeResolver->tracked(merged);
+ Q_ASSERT(merged.isValid());
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
+ m_state.registers[index] = merged;
+ }
+}
+
+void QQmlJSTypePropagator::addReadRegister(int index, const QQmlJSRegisterContent &convertTo)
+{
+ m_state.addReadRegister(index, m_typeResolver->convert(m_state.registers[index], convertTo));
+}
+
void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
{
QStringList errors;
@@ -887,11 +1107,30 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods,
return;
}
- const auto returnType = match.returnType();
- m_state.accumulatorOut = m_typeResolver->globalType(
- returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType());
- if (!m_state.accumulatorOut.isValid())
+ const auto returnType = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(match.returnType());
+ setAccumulator(m_typeResolver->returnType(
+ returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType(),
+ match.isJavaScriptFunction() ? QQmlJSRegisterContent::JavaScriptReturnValue
+ : QQmlJSRegisterContent::MethodReturnValue));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_qs.arg(match.methodName()));
+
+ m_state.setHasSideEffects(true);
+ const auto types = match.parameterTypes();
+ for (int i = 0; i < argc; ++i) {
+ if (i < types.length()) {
+ const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(types.at(i));
+ if (!type.isNull()) {
+ addReadRegister(argv + i, m_typeResolver->globalType(type));
+ continue;
+ }
+ }
+ addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ }
}
void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base, int argc,
@@ -902,6 +1141,7 @@ void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base
void QQmlJSTypePropagator::generate_CallElement(int base, int index, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(base)
Q_UNUSED(index)
Q_UNUSED(argc)
@@ -916,6 +1156,7 @@ void QQmlJSTypePropagator::generate_CallName(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(argc)
Q_UNUSED(argv)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -934,10 +1175,11 @@ void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName,
}
setError(u"method %1 cannot be resolved."_qs.arg(functionName));
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
setError(u"Cannot find function '%1'"_qs.arg(functionName));
- handleUnqualifiedAccess(functionName);
+
+ handleUnqualifiedAccess(functionName, true);
}
void QQmlJSTypePropagator::generate_CallGlobalLookup(int index, int argc, int argv)
@@ -954,6 +1196,7 @@ void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(int index, int
void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -963,6 +1206,7 @@ void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int
void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -972,16 +1216,18 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argv)
Q_UNUSED(argc)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -990,17 +1236,20 @@ void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int
void QQmlJSTypePropagator::generate_SetUnwindHandler(int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindDispatch()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(level)
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -1014,7 +1263,9 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ setAccumulator(QQmlJSRegisterContent());
+ m_state.setHasSideEffects(true);
+ m_state.skipInstructionsUntilNextJumpTarget = true;
}
void QQmlJSTypePropagator::generate_GetException()
@@ -1024,16 +1275,18 @@ void QQmlJSTypePropagator::generate_GetException()
void QQmlJSTypePropagator::generate_SetException()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CreateCallContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
@@ -1041,34 +1294,39 @@ void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
void QQmlJSTypePropagator::generate_PushWithContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushBlockContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CloneBlockContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushScriptContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopScriptContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
@@ -1118,12 +1376,12 @@ void QQmlJSTypePropagator::generate_DeleteName(int name)
void QQmlJSTypePropagator::generate_TypeofName(int name)
{
Q_UNUSED(name);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_TypeofValue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
@@ -1135,9 +1393,10 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(argc == 0
+ ? m_typeResolver->emptyListType()
+ : m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1147,7 +1406,7 @@ void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int
Q_UNUSED(internalClassId)
Q_UNUSED(argc)
Q_UNUSED(args)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1192,36 +1451,40 @@ void QQmlJSTypePropagator::generate_ToObject()
void QQmlJSTypePropagator::generate_Jump(int offset)
{
saveRegisterStateForJump(offset);
- m_state.accumulatorIn = QQmlJSRegisterContent();
- m_state.accumulatorOut = QQmlJSRegisterContent();
m_state.skipInstructionsUntilNextJumpTarget = true;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpTrue(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpFalse(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpNoException(int offset)
{
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
@@ -1232,77 +1495,174 @@ void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
void QQmlJSTypePropagator::generate_CheckException()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
+}
+
+void QQmlJSTypePropagator::recordEqualsNullType()
+{
+ // TODO: We can specialize this further, for QVariant, QJSValue, int, bool, whatever.
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->nullType())
+ || m_typeResolver->containedType(m_state.accumulatorIn())->isReferenceType()) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsIntType()
+{
+ // We have specializations for numeric types and bool.
+ const QQmlJSScope::ConstPtr in = m_typeResolver->containedType(m_state.accumulatorIn());
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->boolType())
+ || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsType(int lhs)
+{
+ const auto isNumericOrEnum = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration() || m_typeResolver->isNumeric(content);
+ };
+
+ const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration()
+ || m_typeResolver->registerContains(content, m_typeResolver->intType());
+ };
+
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const auto lhsRegister = m_state.registers[lhs];
+
+ // If the types are primitive, we compare directly ...
+ if (m_typeResolver->isPrimitive(accumulatorIn)) {
+ if (m_typeResolver->registerContains(
+ accumulatorIn, m_typeResolver->containedType(lhsRegister))) {
+ addReadRegister(lhs, accumulatorIn);
+ addReadAccumulator(accumulatorIn);
+ return;
+ } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) {
+ const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister)
+ ? m_typeResolver->globalType(m_typeResolver->intType())
+ : m_typeResolver->globalType(m_typeResolver->realType());
+ addReadRegister(lhs, targetType);
+ addReadAccumulator(targetType);
+ return;
+ } else if (m_typeResolver->isPrimitive(lhsRegister)) {
+ const QQmlJSRegisterContent primitive = m_typeResolver->globalType(
+ m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, primitive);
+ addReadAccumulator(primitive);
+ }
+ }
+
+ // Otherwise they're both casted to QJSValue.
+ // TODO: We can add more specializations here: void/void null/null object/null etc
+
+ const QQmlJSRegisterContent jsval = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(lhs, jsval);
+ addReadAccumulator(jsval);
+}
+
+void QQmlJSTypePropagator::recordCompareType(int lhs)
+{
+ // If they're both numeric, we can compare them directly.
+ // They may be casted to double, though.
+ const QQmlJSRegisterContent read
+ = (m_typeResolver->isNumeric(m_state.accumulatorIn())
+ && m_typeResolver->isNumeric(m_state.registers[lhs]))
+ ? m_typeResolver->merge(m_state.accumulatorIn(), m_state.registers[lhs])
+ : m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, read);
+ addReadAccumulator(read);
}
void QQmlJSTypePropagator::generate_CmpEqNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpNeNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpEq(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::Equal, lhs);
}
void QQmlJSTypePropagator::generate_CmpNe(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpGt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Gt, lhs);
}
void QQmlJSTypePropagator::generate_CmpGe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Ge, lhs);
}
void QQmlJSTypePropagator::generate_CmpLt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Lt, lhs);
}
void QQmlJSTypePropagator::generate_CmpLe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Le, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictNotEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpIn(int lhs)
{
+ // TODO: Most of the time we don't need the object at all, but only its metatype.
+ // Fix this when we add support for the "in" instruction to the code generator.
+ // Also, specialize on lhs to avoid conversion to QJSPrimitiveValue.
+
+ addReadRegister(lhs, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+
propagateBinaryOperation(QSOperator::Op::In, lhs);
}
@@ -1317,47 +1677,59 @@ void QQmlJSTypePropagator::generate_As(int lhs)
const QQmlJSRegisterContent input = checkedInputRegister(lhs);
QQmlJSScope::ConstPtr contained;
- switch (m_state.accumulatorIn.variant()) {
+ switch (m_state.accumulatorIn().variant()) {
case QQmlJSRegisterContent::ScopeAttached:
+ contained = m_state.accumulatorIn().scopeType();
+ break;
case QQmlJSRegisterContent::MetaType:
- contained = m_state.accumulatorIn.scopeType();
+ contained = m_state.accumulatorIn().scopeType();
+ if (contained->isComposite()) // Otherwise we don't need it
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
break;
default:
- contained = m_typeResolver->containedType(m_state.accumulatorIn);
+ contained = m_typeResolver->containedType(m_state.accumulatorIn());
break;
}
+ addReadRegister(lhs, m_typeResolver->globalType(contained));
+
if (m_typeResolver->containedType(input)->accessSemantics()
!= QQmlJSScope::AccessSemantics::Reference
|| contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
setError(u"invalid cast from %1 to %2. You can only cast object types."_qs
- .arg(input.descriptiveName(), m_state.accumulatorIn.descriptiveName()));
+ .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
} else {
- m_state.accumulatorOut = m_typeResolver->globalType(contained);
+ setAccumulator(m_typeResolver->globalType(contained));
}
}
void QQmlJSTypePropagator::generate_UNot()
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ const QQmlJSRegisterContent boolType = m_typeResolver->globalType(m_typeResolver->boolType());
+ addReadAccumulator(boolType);
+ setAccumulator(boolType);
}
void QQmlJSTypePropagator::generate_UPlus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UMinus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UCompl()
@@ -1367,19 +1739,25 @@ void QQmlJSTypePropagator::generate_UCompl()
void QQmlJSTypePropagator::generate_Increment()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Decrement()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Add(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAnd(int lhs)
@@ -1409,15 +1787,21 @@ void QQmlJSTypePropagator::generate_UShr(int lhs)
void QQmlJSTypePropagator::generate_Shr(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Shl(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAndConst(int rhs)
@@ -1448,18 +1832,22 @@ void QQmlJSTypePropagator::generate_ShrConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_ShlConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Exp(int lhs)
@@ -1470,22 +1858,30 @@ void QQmlJSTypePropagator::generate_Exp(int lhs)
void QQmlJSTypePropagator::generate_Mul(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Div(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Mod(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Sub(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
@@ -1506,45 +1902,53 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
QV4::Moth::ByteCodeHandler::Verdict
-QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
+QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
{
if (m_error->isValid())
return SkipInstruction;
- if (m_state.jumpTargets.contains(currentInstructionOffset()))
- m_state.skipInstructionsUntilNextJumpTarget = false;
- else if (m_state.skipInstructionsUntilNextJumpTarget)
+ if (m_state.jumpTargets.contains(currentInstructionOffset())) {
+ if (m_state.skipInstructionsUntilNextJumpTarget) {
+ // When re-surfacing from dead code, all registers are invalid.
+ m_state.registers.clear();
+ m_state.skipInstructionsUntilNextJumpTarget = false;
+ }
+ } else if (m_state.skipInstructionsUntilNextJumpTarget
+ && !instructionManipulatesContext(type)) {
return SkipInstruction;
-
- bool instructionWritesAccumulatorWithoutReading = false;
- switch (instr) {
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadZero:
- case QV4::Moth::Instr::Type::LoadTrue:
- case QV4::Moth::Instr::Type::LoadFalse:
- case QV4::Moth::Instr::Type::LoadConst:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadUndefined:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadLocal:
- case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::LoadGlobalLookup:
- case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::CallGlobalLookup:
- case QV4::Moth::Instr::Type::CallPropertyLookup:
- instructionWritesAccumulatorWithoutReading = true;
- break;
- default:
- break;
}
const int currentOffset = currentInstructionOffset();
// If we reach an instruction that is a target of a jump earlier, then we must check that the
// register state at the origin matches the current state. If not, then we may have to inject
- // conversion code (communicated to code gen via m_state.expectedTargetTypesBeforeJump). For
+ // conversion code (communicated to code gen via m_state.typeConversions). For
// example:
//
// function blah(x: number) { return x > 10 ? 10 : x}
@@ -1563,11 +1967,6 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
registerIt != end; ++registerIt) {
const int registerIndex = registerIt.key();
- // AccumulatorOut will be the "in" for this (upcoming) instruction, so if that one
- // merely writes to it, we don't care about it's value at the origin of the jump.
- if (registerIndex == Accumulator && instructionWritesAccumulatorWithoutReading)
- continue;
-
auto newType = registerIt.value();
if (!newType.isValid()) {
setError(u"When reached from offset %1, %2 is undefined"_qs
@@ -1578,60 +1977,34 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
auto currentRegister = m_state.registers.find(registerIndex);
if (currentRegister != m_state.registers.end()) {
- // Careful with accessing the hash iterator later inside this block,
- // some operations may invalidate it.
- auto currentRegisterType = currentRegister;
-
- if (*currentRegisterType != newType) {
- auto merged = m_typeResolver->merge(newType, *currentRegisterType);
- Q_ASSERT(merged.isValid());
- m_state.annotations[currentInstructionOffset()]
- .expectedTargetTypesBeforeJump[registerIndex] = merged;
- setRegister(registerIndex, merged);
+ if (currentRegister.value() != newType) {
+ mergeRegister(registerIndex, newType, currentRegister.value());
} else {
// Clear the constant value as this from a jump that might be merging two
// different value
// currentRegister->m_state.value = {};
}
+ } else {
+ mergeRegister(registerIndex, newType, newType);
}
}
}
- // Most instructions require a valid accumulator as input
- switch (instr) {
- case QV4::Moth::Instr::Type::Jump:
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadNull:
- case QV4::Moth::Instr::Type::TypeofName:
- case QV4::Moth::Instr::Type::CallProperty:
- case QV4::Moth::Instr::Type::CallName:
- case QV4::Moth::Instr::Type::MoveReg:
- case QV4::Moth::Instr::Type::MoveConst:
- case QV4::Moth::Instr::Type::DefineArray:
- case QV4::Moth::Instr::Type::DefineObjectLiteral:
- case QV4::Moth::Instr::Type::CheckException:
- case QV4::Moth::Instr::Type::CreateCallContext:
- case QV4::Moth::Instr::Type::PopContext:
- case QV4::Moth::Instr::Type::JumpNoException:
- case QV4::Moth::Instr::Type::SetUnwindHandler:
- case QV4::Moth::Instr::Type::PushCatchContext:
- case QV4::Moth::Instr::Type::UnwindDispatch:
- break;
- default:
- if (instructionWritesAccumulatorWithoutReading)
- m_state.accumulatorIn = QQmlJSRegisterContent();
- else
- m_state.accumulatorIn = checkedInputRegister(Accumulator);
- }
-
return ProcessInstruction;
}
void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
{
+ InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
+ currentInstruction.changedRegister = m_state.changedRegister();
+ currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
+ currentInstruction.readRegisters = m_state.takeReadRegisters();
+ currentInstruction.hasSideEffects = m_state.hasSideEffects();
+ currentInstruction.isRename = m_state.isRename();
+ m_state.setHasSideEffects(false);
+ m_state.setIsRename(false);
+ m_state.setReadRegisters(VirtualRegisters());
+
switch (instr) {
// the following instructions are not expected to produce output in the accumulator
case QV4::Moth::Instr::Type::Ret:
@@ -1653,30 +2026,45 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::SetUnwindHandler:
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
+ if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
+ setError(u"Instruction is not expected to populate the accumulator"_qs);
+ return;
+ }
break;
default:
// If the instruction is expected to produce output, save it in the register set
// for the next instruction.
- if (m_state.accumulatorOut.isValid()) {
- setRegister(Accumulator, m_state.accumulatorOut);
- m_state.accumulatorOut = QQmlJSRegisterContent();
- } else if (!m_error->isValid()) {
+ if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
+ && !m_error->isValid()) {
setError(u"Instruction is expected to populate the accumulator"_qs);
return;
}
}
- m_state.annotations[currentInstructionOffset()].registers = m_state.registers;
+ if (m_state.changedRegisterIndex() != InvalidRegister) {
+ Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
+ m_state.registers[m_state.changedRegisterIndex()] = m_state.changedRegister();
+ m_state.clearChangedRegister();
+ }
}
-void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
+QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
if (!lhsRegister.isValid())
- return;
+ return QQmlJSRegisterContent();
- m_state.accumulatorOut =
- m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ op, lhsRegister, m_state.accumulatorIn());
+
+ setAccumulator(type);
+
+ // If we're dealing with QJSPrimitiveType, do not force premature conversion of the arguemnts
+ // to the target type. Such an operation can lose information.
+ if (type.storedType() == m_typeResolver->jsPrimitiveType())
+ return m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+
+ return type;
}
void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
@@ -1692,8 +2080,10 @@ void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
const auto registerStates =
m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
for (auto it = registerStates.first; it != registerStates.second; ++it) {
- if (it->registers == state.registers)
+ if (it->registers.keys() == state.registers.keys()
+ && it->registers.values() == state.registers.values()) {
return; // We've seen the same register state before. No need for merging.
+ }
}
// The register state at the target offset needs to be resolved in a further pass.
@@ -1715,28 +2105,14 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const
registerIndex - FirstArgument - m_function->argumentTypes.count());
}
-// As the source register content may also be a register, we expect a copy here,
-// rather than a reference. Otherwise you might pass a reference to another entry
-// of m_state.registers, which then becomes invalid when making space for the new
-// entry in the hash.
-void QQmlJSTypePropagator::setRegister(int index, QQmlJSRegisterContent content)
-{
- m_state.registers[index] = std::move(content);
-}
-
-void QQmlJSTypePropagator::setRegister(int index, const QQmlJSScope::ConstPtr &content)
-{
- m_state.registers[index] = m_typeResolver->globalType(content);
-}
-
QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
{
- VirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
- if (regIt == m_state.registers.constEnd()) {
+ const auto regIt = m_state.registers.find(reg);
+ if (regIt == m_state.registers.end()) {
setError(u"Type error: could not infer the type of an expression"_qs);
return {};
}
- return *regIt;
+ return regIt.value();
}
bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index 1b3af43f60..8cc6879f09 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -208,13 +208,15 @@ private:
bool needsMorePasses = false;
};
- void handleUnqualifiedAccess(const QString &name) const;
+ void handleUnqualifiedAccess(const QString &name, bool isMethod) const;
void checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name, bool isMethod) const;
- bool checkRestricted(const QString &propertyName) const;
+ bool isRestricted(const QString &propertyName) const;
+ bool isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const;
+ bool isMissingPropertyType(QQmlJSScope::ConstPtr scope, const QString &type) const;
QQmlJS::SourceLocation getCurrentSourceLocation() const;
QQmlJS::SourceLocation getCurrentBindingSourceLocation() const;
- void propagateBinaryOperation(QSOperator::Op op, int lhs);
+ QQmlJSRegisterContent propagateBinaryOperation(QSOperator::Op op, int lhs);
void propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv);
void propagatePropertyLookup(const QString &name);
void propagateScopeLookupCall(const QString &functionName, int argc, int argv);
@@ -223,19 +225,32 @@ private:
QString registerName(int registerIndex) const;
- void setRegister(int index, QQmlJSRegisterContent content);
- void setRegister(int index, const QQmlJSScope::ConstPtr &content);
-
QQmlJSRegisterContent checkedInputRegister(int reg);
QQmlJSMetaMethod bestMatchForCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
QStringList *errors);
+ void setAccumulator(const QQmlJSRegisterContent &content);
+ void setRegister(int index, const QQmlJSRegisterContent &content);
+ void mergeRegister(int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b);
+
+ void addReadRegister(int index, const QQmlJSRegisterContent &convertTo);
+ void addReadAccumulator(const QQmlJSRegisterContent &convertTo)
+ {
+ addReadRegister(Accumulator, convertTo);
+ }
+
+ void recordEqualsNullType();
+ void recordEqualsIntType();
+ void recordEqualsType(int lhs);
+ void recordCompareType(int lhs);
+
QQmlJSRegisterContent m_returnType;
QQmlJSTypeInfo *m_typeInfo = nullptr;
// Not part of the state, as the back jumps are the reason for running multiple passes
QMultiHash<int, ExpectedRegisterState> m_jumpOriginRegisterStateByTargetInstructionOffset;
+ InstructionAnnotations m_prevStateAnnotations;
PassState m_state;
};
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 99de711ee6..1b0520e70d 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -64,6 +64,7 @@ static bool searchBaseAndExtensionTypes(const QQmlJSScope::ConstPtr type, const
}
QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
+ : m_typeTracker(std::make_unique<TypeTracker>())
{
const QQmlJSImporter::ImportedTypes builtinTypes = importer->builtinInternalNames();
m_voidType = builtinTypes[u"void"_qs].scope;
@@ -73,27 +74,33 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_intType = builtinTypes[u"int"_qs].scope;
m_boolType = builtinTypes[u"bool"_qs].scope;
m_stringType = builtinTypes[u"QString"_qs].scope;
+ m_stringListType = builtinTypes[u"QStringList"_qs].scope;
m_urlType = builtinTypes[u"QUrl"_qs].scope;
m_dateTimeType = builtinTypes[u"QDateTime"_qs].scope;
m_variantListType = builtinTypes[u"QVariantList"_qs].scope;
m_varType = builtinTypes[u"QVariant"_qs].scope;
m_jsValueType = builtinTypes[u"QJSValue"_qs].scope;
+ QQmlJSScope::Ptr emptyListType = QQmlJSScope::create();
+ emptyListType->setInternalName(u"void*"_qs);
+ emptyListType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
+ m_emptyListType = emptyListType;
+
QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_qs);
- jsPrimitiveType->setFileName(u"qjsprimitivevalue.h"_qs);
+ jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_qs);
jsPrimitiveType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
m_jsPrimitiveType = jsPrimitiveType;
QQmlJSScope::Ptr listPropertyType = QQmlJSScope::create();
listPropertyType->setInternalName(u"QQmlListProperty<QObject>"_qs);
- listPropertyType->setFileName(u"qqmllist.h"_qs);
+ listPropertyType->setFilePath(u"qqmllist.h"_qs);
listPropertyType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
m_listPropertyType = listPropertyType;
QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create();
metaObjectType->setInternalName(u"const QMetaObject"_qs);
- metaObjectType->setFileName(u"qmetaobject.h"_qs);
+ metaObjectType->setFilePath(u"qmetaobject.h"_qs);
metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
m_metaObjectType = metaObjectType;
@@ -137,19 +144,19 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
// If the property is defined in the same scope where it is set,
// we are in fact allowed to set it, even if it's not writable.
if (!property.isWritable() && !scope->hasOwnProperty(binding.propertyName())) {
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs
- .arg(binding.propertyName()),
- Log_Type, binding.sourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(
+ binding.propertyName()),
+ Log_Type, binding.sourceLocation());
continue;
}
- if (!canConvertFromTo(binding.literalType(), property.type())) {
- m_logger->logWarning(u"Cannot assign binding of type %1 to %2"_qs
- .arg(binding.literalTypeName())
- .arg(property.typeName()),
- Log_Type, binding.sourceLocation());
- } else if (property.type() == m_stringType && isNumeric(binding.literalType())) {
- m_logger->logWarning(u"Cannot assign a numeric constant to a string property"_qs,
- Log_Type, binding.sourceLocation());
+ if (!canConvertFromTo(binding.literalType(this), property.type())) {
+ m_logger->log(u"Cannot assign binding of type %1 to %2"_qs
+ .arg(binding.literalTypeName(), property.typeName()),
+ Log_Type, binding.sourceLocation());
+ } else if (equals(property.type(), m_stringType)
+ && isNumeric(binding.literalType(this))) {
+ m_logger->log(u"Cannot assign a numeric constant to a string property"_qs,
+ Log_Type, binding.sourceLocation());
}
}
}
@@ -170,6 +177,33 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
return m_objectsById.scope(id, referrer);
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &elementType) const
+{
+ auto it = m_typeTracker->listTypes.find(elementType);
+ if (it != m_typeTracker->listTypes.end())
+ return *it;
+
+ switch (elementType->accessSemantics()) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ return m_listPropertyType;
+ case QQmlJSScope::AccessSemantics::Value: {
+ QQmlJSScope::Ptr listType = QQmlJSScope::create();
+ listType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
+ listType->setValueTypeName(elementType->internalName());
+ listType->setInternalName(u"QList<%1>"_qs.arg(elementType->internalName()));
+ listType->setFilePath(elementType->filePath());
+ const QQmlJSImportedScope element = {elementType, QTypeRevision()};
+ QQmlJSScope::resolveTypes(listType, {{elementType->internalName(), element}});
+ Q_ASSERT(equals(listType->valueType(), elementType));
+ m_typeTracker->listTypes[elementType] = listType;
+ return listType;
+ }
+ default:
+ break;
+ }
+ return QQmlJSScope::ConstPtr();
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
{
return m_imports[QmlIR::IRBuilder::asString(type->typeId)].scope;
@@ -224,12 +258,12 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::Add: {
const auto leftContents = containedType(left);
const auto rightContents = containedType(right);
- if (leftContents == stringType() || rightContents == stringType())
+ if (equals(leftContents, stringType()) || equals(rightContents, stringType()))
return QQmlJSRegisterContent::create(stringType(), stringType(),
QQmlJSRegisterContent::Builtin);
const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -241,7 +275,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
}
case QSOperator::Op::Sub: {
const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right));
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -267,9 +301,8 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
return merge(left, right);
}
-QQmlJSRegisterContent
-QQmlJSTypeResolver::typeForUnaryOperation(UnaryOperator oper,
- const QQmlJSRegisterContent &operand) const
+QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
+ UnaryOperator oper, const QQmlJSRegisterContent &operand) const
{
// For now, we are only concerned with the unary arithmetic operators.
// The boolean and bitwise ones are special cased elsewhere.
@@ -291,16 +324,16 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
- return type == m_intType || type == m_realType || type == m_floatType || type == m_boolType
- || type == m_voidType || type == m_nullType || type == m_stringType
- || type == m_jsPrimitiveType;
+ return equals(type, m_intType) || equals(type, m_realType) || equals(type, m_floatType)
+ || equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType)
+ || equals(type, m_stringType) || equals(type, m_jsPrimitiveType);
}
bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
{
return searchBaseAndExtensionTypes(
type, [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension) {
- return scope == m_numberPrototype;
+ return equals(scope, m_numberPrototype);
});
}
@@ -311,19 +344,162 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
return container.type();
if (container.isProperty()) {
const QQmlJSMetaProperty prop = container.property();
- return prop.isList() ? listPropertyType() : QQmlJSScope::ConstPtr(prop.type());
+ return prop.isList() ? listType(prop.type()) : QQmlJSScope::ConstPtr(prop.type());
}
if (container.isEnumeration())
return container.enumeration().type();
if (container.isMethod())
- return jsValueType();
- if (container.isImportNamespace())
- return container.scopeType();
+ return container.storedType(); // Methods can only be stored in QJSValue.
+ if (container.isImportNamespace()) {
+ switch (container.variant()) {
+ case QQmlJSRegisterContent::ScopeModulePrefix:
+ return container.storedType(); // We don't store scope module prefixes
+ case QQmlJSRegisterContent::ObjectModulePrefix:
+ return container.scopeType(); // We need to pass the original object through.
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+ if (container.isConversion())
+ return container.conversionResult();
+
+ Q_UNREACHABLE();
+ return {};
+}
+
+void QQmlJSTypeResolver::trackListPropertyType(
+ const QQmlJSScope::ConstPtr &trackedListElementType) const
+{
+ if (m_typeTracker->trackedTypes.contains(trackedListElementType)
+ && !m_typeTracker->listTypes.contains(trackedListElementType)) {
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(m_listPropertyType);
+ m_typeTracker->listTypes[trackedListElementType] = clone;
+ m_typeTracker->trackedTypes[clone] = { m_listPropertyType, QQmlJSScope::ConstPtr(), clone };
+ }
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedType(const QQmlJSScope::ConstPtr &type) const
+{
+ // If origin is in fact an already tracked type, track the original of that one instead.
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ QQmlJSScope::ConstPtr orig = (it == m_typeTracker->trackedTypes.end()) ? type : it->original;
+
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(orig);
+ m_typeTracker->trackedTypes[clone] = { std::move(orig), QQmlJSScope::ConstPtr(), clone };
+ return clone;
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
+ const QQmlJSRegisterContent &origin,
+ QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const
+{
+ if (origin.isType()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), (this->*op)(origin.type()),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isProperty()) {
+ QQmlJSMetaProperty prop = origin.property();
+ prop.setType((this->*op)(prop.type()));
+ if (prop.isList())
+ trackListPropertyType(prop.type());
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), prop,
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isEnumeration()) {
+ QQmlJSMetaEnum enumeration = origin.enumeration();
+ enumeration.setType((this->*op)(enumeration.type()));
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), enumeration, origin.enumMember(),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isMethod()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.method(), origin.variant(),
+ (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isImportNamespace()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.importNamespace(),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isConversion()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.conversionOrigins(),
+ (this->*op)(origin.conversionResult()),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
Q_UNREACHABLE();
return {};
}
+QQmlJSRegisterContent QQmlJSTypeResolver::original(const QQmlJSRegisterContent &type) const
+{
+ return transformed(type, &QQmlJSTypeResolver::originalType);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::tracked(const QQmlJSRegisterContent &type) const
+{
+ return transformed(type, &QQmlJSTypeResolver::trackedType);
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedContainedType(
+ const QQmlJSRegisterContent &container) const
+{
+ const QQmlJSScope::ConstPtr type = containedType(container);
+ return m_typeTracker->trackedTypes.contains(type) ? type : QQmlJSScope::ConstPtr();
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
+ const QQmlJSRegisterContent &container) const
+{
+ return originalType(containedType(container));
+}
+
+void QQmlJSTypeResolver::adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(tracked);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ it->replacement = comparableType(conversion);
+ *it->clone = std::move(*QQmlJSScope::clone(conversion));
+}
+
+void QQmlJSTypeResolver::adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked, const QList<QQmlJSScope::ConstPtr> &conversions) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(tracked);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ QQmlJSScope::Ptr mutableTracked = it->clone;
+ QQmlJSScope::ConstPtr result;
+ for (const QQmlJSScope::ConstPtr &type : conversions)
+ result = merge(type, result);
+
+ // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
+ // we better not change the type.
+ if (canPrimitivelyConvertFromTo(tracked, result)) {
+ it->replacement = comparableType(result);
+ *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ }
+}
+
+void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ *it->clone = std::move(*QQmlJSScope::clone(genericType(type)));
+ if (it->replacement)
+ it->replacement = genericType(it->replacement);
+ it->original = genericType(it->original);
+}
+
QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &container) const
{
QQmlJSScope::ConstPtr type;
@@ -345,69 +521,28 @@ QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &conta
bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
- // ### need a generic solution for custom cpp types:
- // if (from->m_hasBoolOverload && to == boolType)
- // return true;
-
- if (from == to)
- return true;
- if (from == m_varType || to == m_varType)
- return true;
- if (from == m_jsValueType || to == m_jsValueType)
- return true;
- if (isNumeric(from) && isNumeric(to))
- return true;
- if (from == m_intType && to == m_boolType)
- return true;
- if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference && to == m_boolType)
- return true;
-
- // Yes, our String has number constructors.
- if (isNumeric(from) && to == m_stringType)
+ if (canPrimitivelyConvertFromTo(from, to))
return true;
- // We can always convert between strings and urls.
- if ((from == m_stringType && to == m_urlType) || (from == m_urlType && to == m_stringType))
- return true;
+ // ### need a generic solution for custom cpp types:
+ // if (from->m_hasBoolOverload && equals(to, boolType))
+ // return true;
// All of these types have QString conversions that require a certain format
- // TODO: Actually verify these strings or deprecate them
- if (from == m_stringType && !to.isNull()) {
+ // TODO: Actually verify these strings or deprecate them.
+ // Some of those type are builtins or should be builtins. We should add code for them
+ // in QQmlJSCodeGenerator::conversion().
+ if (equals(from, m_stringType) && !to.isNull()) {
const QString toTypeName = to->internalName();
- if (to == m_dateTimeType || toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
- || toTypeName == u"QPoint"_qs || toTypeName == u"QPointF"_qs
- || toTypeName == u"QSize"_qs || toTypeName == u"QSizeF"_qs || toTypeName == u"QRect"_qs
- || toTypeName == u"QRectF"_qs || toTypeName == u"QColor"_qs) {
+ if (toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
+ || toTypeName == u"QPoint"_qs || toTypeName == u"QPointF"_qs
+ || toTypeName == u"QSize"_qs || toTypeName == u"QSizeF"_qs
+ || toTypeName == u"QRect"_qs || toTypeName == u"QRectF"_qs
+ || toTypeName == u"QColor"_qs) {
return true;
}
}
- if (from == m_voidType)
- return true;
-
- if (to.isNull())
- return false;
-
- if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- return true;
-
- if (from == m_jsPrimitiveType) {
- // You can cast any primitive to a nullptr
- return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
- }
-
- if (to == m_jsPrimitiveType)
- return isPrimitive(from);
-
- const bool matchByName = !to->isComposite();
- Q_ASSERT(!matchByName || !to->internalName().isEmpty());
- for (auto baseType = from; baseType; baseType = baseType->baseType()) {
- if (baseType == to)
- return true;
- if (matchByName && baseType->internalName() == to->internalName())
- return true;
- }
-
return false;
}
@@ -426,38 +561,61 @@ static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent
QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
const QQmlJSRegisterContent &b) const
{
- return QQmlJSRegisterContent::create(
- merge(a.storedType(), b.storedType()),
- merge(containedType(a), containedType(b)), mergeVariants(a.variant(), b.variant()),
- merge(a.scopeType(), b.scopeType()));
-}
+ QList<QQmlJSScope::ConstPtr> origins;
+ if (a.isConversion())
+ origins.append(a.conversionOrigins());
+ else
+ origins.append(containedType(a));
-static QQmlJSScope::ConstPtr commonBaseType(const QQmlJSScope::ConstPtr &a,
- const QQmlJSScope::ConstPtr &b)
-{
- for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
- for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
- if (aBase == bBase)
- return aBase;
- }
- }
+ if (b.isConversion())
+ origins.append(b.conversionOrigins());
+ else
+ origins.append(containedType(b));
- return {};
+ std::sort(origins.begin(), origins.end());
+ const auto erase = std::unique(origins.begin(), origins.end());
+ origins.erase(erase, origins.end());
+
+ return QQmlJSRegisterContent::create(
+ merge(a.storedType(), b.storedType()),
+ origins,
+ merge(containedType(a), containedType(b)),
+ mergeVariants(a.variant(), b.variant()),
+ merge(a.scopeType(), b.scopeType()));
}
QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const
{
- if (a == b)
+ if (a.isNull())
+ return b;
+
+ if (b.isNull())
+ return a;
+
+ const auto commonBaseType = [this](
+ const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
+ for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
+ for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
+ if (equals(aBase, bBase))
+ return aBase;
+ }
+ }
+
+ return QQmlJSScope::ConstPtr();
+ };
+
+
+ if (equals(a, b))
return a;
- if (a == jsValueType() || a == varType())
+ if (equals(a, jsValueType()) || equals(a, varType()))
return a;
- if (b == jsValueType() || b == varType())
+ if (equals(b, jsValueType()) || equals(b, varType()))
return b;
auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) {
- return (a == from && b == to) || (b == from && a == to);
+ return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to));
};
if (isNumeric(a) && isNumeric(b))
@@ -473,22 +631,44 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (auto commonBase = commonBaseType(a, b))
return commonBase;
- if (a == nullType() && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(a, nullType()) && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return b;
- if (b == nullType() && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(b, nullType()) && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return a;
return varType();
}
+bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content) const
+{
+ const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
+ return equals(type, m_voidType) || equals(type, m_varType)
+ || equals(type, m_jsValueType) || equals(type, m_jsPrimitiveType);
+ };
+
+ if (!canBeUndefined(content.storedType()))
+ return false;
+
+ if (!content.isConversion())
+ return canBeUndefined(containedType(content));
+
+ const auto origins = content.conversionOrigins();
+ for (const auto &origin : origins) {
+ if (canBeUndefined(origin))
+ return true;
+ }
+
+ return false;
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPtr &type,
ComponentIsGeneric allowComponent) const
{
if (type->isScript())
return m_jsValueType;
- if (type == m_metaObjectType)
+ if (equals(type, m_metaObjectType))
return m_metaObjectType;
if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
@@ -506,21 +686,19 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
}
}
- m_logger->logWarning(u"Object type %1 is not derived from QObject or QQmlComponent"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
// Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
// We don't want to deal with those, but m_jsValueType is the best generic option.
return m_jsValueType;
}
- if (type == voidType())
- return jsPrimitiveType();
-
- if (isPrimitive(type) || type == m_jsValueType || type == m_listPropertyType
- || type == m_urlType || type == m_dateTimeType || type == m_variantListType
- || type == m_varType) {
+ if (isPrimitive(type) || equals(type, m_jsValueType) || equals(type, m_listPropertyType)
+ || equals(type, m_urlType) || equals(type, m_dateTimeType)
+ || equals(type, m_variantListType) || equals(type, m_varType)
+ || equals(type, m_stringListType) || equals(type, m_emptyListType)) {
return type;
}
@@ -530,6 +708,10 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
if (type->scopeType() == QQmlJSScope::EnumScope)
return m_intType;
+ if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ return listType(genericType(type->valueType()));
+ }
+
return m_varType;
}
@@ -552,46 +734,6 @@ scopeContentVariant(QQmlJSTypeResolver::BaseOrExtension mode, bool isMethod)
return QQmlJSRegisterContent::Unknown;
}
-static bool isAssignedToDefaultProperty(const QQmlJSScope::ConstPtr &parent,
- const QQmlJSScope::ConstPtr &child)
-{
- QString defaultPropertyName;
- QQmlJSMetaProperty defaultProperty;
- if (!searchBaseAndExtensionTypes(
- parent, [&](const QQmlJSScope::ConstPtr &scope,
- QQmlJSTypeResolver::BaseOrExtension mode) {
- Q_UNUSED(mode);
- defaultPropertyName = scope->defaultPropertyName();
- defaultProperty = scope->property(defaultPropertyName);
- return !defaultPropertyName.isEmpty();
- })) {
- return false;
- }
-
- QQmlJSScope::ConstPtr bindingHolder = parent;
- while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
- // Only traverse the base type hierarchy here, not the extended types.
- // Extensions cannot hold bindings.
- bindingHolder = bindingHolder->baseType();
-
- // Consequently, the default property may be inaccessibly
- // hidden in some extension via shadowing.
- // Nothing can be assigned to it then.
- if (!bindingHolder)
- return false;
- }
-
- const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
- = bindingHolder->propertyBindings(defaultPropertyName);
- for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
- && binding.objectType() == child) {
- return true;
- }
- }
- return false;
-}
-
static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isComposite());
@@ -612,6 +754,46 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s
QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
const QString &name) const
{
+ const auto isAssignedToDefaultProperty = [this](
+ const QQmlJSScope::ConstPtr &parent, const QQmlJSScope::ConstPtr &child) {
+ QString defaultPropertyName;
+ QQmlJSMetaProperty defaultProperty;
+ if (!searchBaseAndExtensionTypes(
+ parent, [&](const QQmlJSScope::ConstPtr &scope,
+ QQmlJSTypeResolver::BaseOrExtension mode) {
+ Q_UNUSED(mode);
+ defaultPropertyName = scope->defaultPropertyName();
+ defaultProperty = scope->property(defaultPropertyName);
+ return !defaultPropertyName.isEmpty();
+ })) {
+ return false;
+ }
+
+ QQmlJSScope::ConstPtr bindingHolder = parent;
+ while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
+ // Only traverse the base type hierarchy here, not the extended types.
+ // Extensions cannot hold bindings.
+ bindingHolder = bindingHolder->baseType();
+
+ // Consequently, the default property may be inaccessibly
+ // hidden in some extension via shadowing.
+ // Nothing can be assigned to it then.
+ if (!bindingHolder)
+ return false;
+ }
+
+ const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
+ = bindingHolder->propertyBindings(defaultPropertyName);
+ for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
+ if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
+ && equals(binding.objectType(), child)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
if (QQmlJSScope::ConstPtr identified = scopeForId(name, scope)) {
return QQmlJSRegisterContent::create(storedType(identified), identified,
QQmlJSRegisterContent::ObjectById, scope);
@@ -673,15 +855,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
if (const auto attached = type->attachedType()) {
if (!genericType(attached)) {
- m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg(
- attached->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot resolve generic base of attached %1"_qs.arg(
+ attached->internalName()),
+ Log_Compiler, attached->sourceLocation());
return {};
} else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
- u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
return {};
} else {
// We don't know yet whether we need the attached or the plain object. In direct
@@ -746,6 +927,71 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
return false;
}
+bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
+ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
+{
+ if (equals(from, to))
+ return true;
+ if (equals(from, m_varType) || equals(to, m_varType))
+ return true;
+ if (equals(from, m_jsValueType) || equals(to, m_jsValueType))
+ return true;
+ if (isNumeric(from) && isNumeric(to))
+ return true;
+ if (equals(from, m_intType) && equals(to, m_boolType))
+ return true;
+ if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
+ && equals(to, m_boolType)) {
+ return true;
+ }
+
+ // Yes, our String has number constructors.
+ if (isNumeric(from) && equals(to, m_stringType))
+ return true;
+
+ // We can always convert between strings and urls.
+ if ((equals(from, m_stringType) && equals(to, m_urlType))
+ || (equals(from, m_urlType) && equals(to, m_stringType))) {
+ return true;
+ }
+
+ if (equals(from, m_voidType) || equals(to, m_voidType))
+ return true;
+
+ if (to.isNull())
+ return false;
+
+ if (equals(from, m_stringType) && equals(to, m_dateTimeType))
+ return true;
+
+ if (equals(from, m_nullType)
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ return true;
+ }
+
+ if (equals(from, m_jsPrimitiveType)) {
+ // You can cast any primitive to a nullptr
+ return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
+ }
+
+ if (equals(to, m_jsPrimitiveType))
+ return isPrimitive(from);
+
+ if (equals(from, m_emptyListType))
+ return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
+
+ const bool matchByName = !to->isComposite();
+ Q_ASSERT(!matchByName || !to->internalName().isEmpty());
+ for (auto baseType = from; baseType; baseType = baseType->baseType()) {
+ if (equals(baseType, to))
+ return true;
+ if (matchByName && baseType->internalName() == to->internalName())
+ return true;
+ }
+
+ return false;
+}
+
QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
bool isWritable, const QQmlJSScope::ConstPtr &scope) const
{
@@ -762,7 +1008,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
{
QQmlJSRegisterContent result;
- if (type == jsValueType()) {
+ if (equals(type, jsValueType())) {
QQmlJSMetaProperty prop;
prop.setPropertyName(name);
prop.setTypeName(u"QJSValue"_qs);
@@ -772,9 +1018,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
QQmlJSRegisterContent::JavaScriptObjectProperty, type);
}
- if ((type == stringType() || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ if ((equals(type, stringType())
+ || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
&& name == u"length"_qs) {
- return lengthProperty(type != stringType(), type);
+ return lengthProperty(!equals(type, stringType()), type);
}
const auto check = [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension mode) {
@@ -821,15 +1068,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
if (!genericType(attached)) {
- m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg(
- attached->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot resolve generic base of attached %1"_qs.arg(
+ attached->internalName()),
+ Log_Compiler, attached->sourceLocation());
return {};
} else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
- u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
return {};
} else {
return QQmlJSRegisterContent::create(storedType(attached), attached,
@@ -895,19 +1141,22 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
}
if (type.isImportNamespace()) {
if (type.scopeType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
+ m_logger->log(
u"Cannot use non-reference type %1 as base of namespaced attached type"_qs.arg(
type.scopeType()->internalName()),
- Log_Type);
+ Log_Type, type.scopeType()->sourceLocation());
return {};
}
if (QQmlJSScope::ConstPtr result = typeForName(name)) {
QQmlJSScope::ConstPtr attached = result->attachedType();
if (attached && genericType(attached)) {
- return QQmlJSRegisterContent::create(storedType(attached), attached,
- QQmlJSRegisterContent::ObjectAttached,
- result);
+ return QQmlJSRegisterContent::create(
+ storedType(attached), attached,
+ type.variant() == QQmlJSRegisterContent::ObjectModulePrefix
+ ? QQmlJSRegisterContent::ObjectAttached
+ : QQmlJSRegisterContent::ScopeAttached,
+ result);
}
if (result->isSingleton()) {
@@ -919,6 +1168,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return {};
}
+ if (type.isConversion()) {
+ const auto result = memberType(type.conversionResult(), name);
+ return result.isValid() ? result : memberEnumType(type.scopeType(), name);
+ }
Q_UNREACHABLE();
return {};
@@ -932,7 +1185,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
auto valueType = [this](const QQmlJSScope::ConstPtr &scope) {
if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return scope->valueType();
- else if (scope == m_jsValueType || scope == m_varType)
+ else if (equals(scope, m_jsValueType) || equals(scope, m_varType))
return m_jsValueType;
return QQmlJSScope::ConstPtr();
};
@@ -940,6 +1193,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
if (listType.isType()) {
scope = listType.type();
value = valueType(scope);
+ } else if (listType.isConversion()) {
+ value = valueType(listType.conversionResult());
} else if (listType.isProperty()) {
const auto prop = listType.property();
if (prop.isList()) {
@@ -970,19 +1225,35 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
return QQmlJSRegisterContent::create(stored, property, QQmlJSRegisterContent::ListValue, scope);
}
+QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
+ const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant) const
+{
+ Q_ASSERT(variant == QQmlJSRegisterContent::MethodReturnValue
+ || variant == QQmlJSRegisterContent::JavaScriptReturnValue);
+ return QQmlJSRegisterContent::create(storedType(type), type, variant);
+}
+
+bool QQmlJSTypeResolver::registerIsStoredIn(
+ const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(reg.storedType(), type);
+}
+
bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const
{
if (reg.isType())
- return reg.type() == type;
+ return equals(reg.type(), type);
+ if (reg.isConversion())
+ return equals(reg.conversionResult(), type);
if (reg.isProperty()) {
const auto prop = reg.property();
- return prop.isList() ? type == listPropertyType() : prop.type() == type;
+ return prop.isList() ? equals(type, listPropertyType()) : equals(prop.type(), type);
}
if (reg.isEnumeration())
- return type == intType();
+ return equals(type, intType());
if (reg.isMethod())
- return type == jsValueType();
+ return equals(type, jsValueType());
return false;
}
@@ -990,8 +1261,8 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
{
if (type.isNull())
return {};
- if (type == voidType())
- return jsPrimitiveType();
+ if (equals(type, voidType()))
+ return type;
if (type->isScript())
return jsValueType();
if (type->isComposite()) {
@@ -1001,9 +1272,50 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
// If we can't find the non-composite base, we really don't know what it is.
return genericType(type);
}
- if (type->fileName().isEmpty())
+ if (type->filePath().isEmpty())
return genericType(type);
return type;
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ return it == m_typeTracker->trackedTypes.end() ? type : it->original;
+}
+
+/*!
+ * \internal
+ *
+ * Compares the origin types of \a a and \a b. A straight a == b would compare the identity
+ * of the pointers. However, since we clone types to keep track of them, we need a separate
+ * way to compare the clones. Usually you'd do *a == *b for that, but as QQmlJSScope is rather
+ * large, we offer an optimization here that uses the type tracking we already have in place.
+ */
+bool QQmlJSTypeResolver::equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
+{
+ return comparableType(a) == comparableType(b);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::convert(
+ const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const
+{
+ if (from.isConversion()) {
+ return QQmlJSRegisterContent::create(
+ to.storedType(), from.conversionOrigins(), containedType(to), from.variant(),
+ from.scopeType());
+ }
+
+ return QQmlJSRegisterContent::create(
+ to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
+ containedType(to), from.variant(), from.scopeType());
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::comparableType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.constFind(type);
+ if (it == m_typeTracker->trackedTypes.constEnd())
+ return type;
+ return it->replacement ? it->replacement : it->original;
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index babc0c6c22..32c64b89f7 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -62,12 +62,14 @@ public:
void init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program);
QQmlJSScope::ConstPtr voidType() const { return m_voidType; }
+ QQmlJSScope::ConstPtr emptyListType() const { return m_emptyListType; }
QQmlJSScope::ConstPtr nullType() const { return m_nullType; }
QQmlJSScope::ConstPtr realType() const { return m_realType; }
QQmlJSScope::ConstPtr floatType() const { return m_floatType; }
QQmlJSScope::ConstPtr intType() const { return m_intType; }
QQmlJSScope::ConstPtr boolType() const { return m_boolType; }
QQmlJSScope::ConstPtr stringType() const { return m_stringType; }
+ QQmlJSScope::ConstPtr stringListType() const { return m_stringListType; }
QQmlJSScope::ConstPtr urlType() const { return m_urlType; }
QQmlJSScope::ConstPtr dateTimeType() const { return m_dateTimeType; }
QQmlJSScope::ConstPtr variantListType() const { return m_variantListType; }
@@ -87,6 +89,7 @@ public:
return m_imports.contains(name) && !m_imports[name].scope;
}
+ QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType) const;
QQmlJSScope::ConstPtr typeForName(const QString &name) const { return m_imports[name].scope; }
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
@@ -96,8 +99,8 @@ public:
enum class UnaryOperator { Plus, Minus, Increment, Decrement };
- QQmlJSRegisterContent typeForUnaryOperation(UnaryOperator oper,
- const QQmlJSRegisterContent &operand) const;
+ QQmlJSRegisterContent typeForArithmeticUnaryOperation(
+ UnaryOperator oper, const QQmlJSRegisterContent &operand) const;
bool isPrimitive(const QQmlJSRegisterContent &type) const;
bool isNumeric(const QQmlJSRegisterContent &type) const;
@@ -116,17 +119,35 @@ public:
QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name) const;
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const;
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &listType) const;
+ QQmlJSRegisterContent returnType(const QQmlJSScope::ConstPtr &type,
+ QQmlJSRegisterContent::ContentVariant variant) const;
+ bool registerIsStoredIn(const QQmlJSRegisterContent &reg,
+ const QQmlJSScope::ConstPtr &type) const;
bool registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const;
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const;
QString containedTypeName(const QQmlJSRegisterContent &container) const;
+ QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &type) const;
+ QQmlJSRegisterContent original(const QQmlJSRegisterContent &type) const;
+
+ QQmlJSScope::ConstPtr trackedContainedType(const QQmlJSRegisterContent &container) const;
+ QQmlJSScope::ConstPtr originalContainedType(const QQmlJSRegisterContent &container) const;
+
+ void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
+ const QQmlJSScope::ConstPtr &conversion) const;
+ void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
+ const QList<QQmlJSScope::ConstPtr> &conversions) const;
+ void generalizeType(const QQmlJSScope::ConstPtr &type) const;
+
void setParentMode(ParentMode mode) { m_parentMode = mode; }
ParentMode parentMode() const { return m_parentMode; }
- QQmlJSScope::ConstPtr
- storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr originalType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr trackedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr comparableType(const QQmlJSScope::ConstPtr &type) const;
const QQmlJSScopesById &objectsById() const { return m_objectsById; }
const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers() const
@@ -134,10 +155,17 @@ public:
return m_signalHandlers;
}
-protected:
+ bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const;
+
+ QQmlJSRegisterContent convert(
+ const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const;
+
QQmlJSScope::ConstPtr merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const;
+ bool canHoldUndefined(const QQmlJSRegisterContent &content) const;
+protected:
+
QQmlJSRegisterContent memberType(const QQmlJSScope::ConstPtr &type, const QString &name) const;
QQmlJSRegisterContent memberEnumType(const QQmlJSScope::ConstPtr &type,
const QString &name) const;
@@ -145,9 +173,16 @@ protected:
bool isNumeric(const QQmlJSScope::ConstPtr &type) const;
bool checkEnums(const QQmlJSScope::ConstPtr &scope, const QString &name,
QQmlJSRegisterContent *result, BaseOrExtension mode) const;
+ bool canPrimitivelyConvertFromTo(
+ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const;
QQmlJSRegisterContent lengthProperty(bool isWritable, const QQmlJSScope::ConstPtr &scope) const;
+ void trackListPropertyType(const QQmlJSScope::ConstPtr &trackedListElementType) const;
+ QQmlJSRegisterContent transformed(
+ const QQmlJSRegisterContent &origin,
+ QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const;
QQmlJSScope::ConstPtr m_voidType;
+ QQmlJSScope::ConstPtr m_emptyListType;
QQmlJSScope::ConstPtr m_nullType;
QQmlJSScope::ConstPtr m_numberPrototype;
QQmlJSScope::ConstPtr m_realType;
@@ -155,6 +190,7 @@ protected:
QQmlJSScope::ConstPtr m_intType;
QQmlJSScope::ConstPtr m_boolType;
QQmlJSScope::ConstPtr m_stringType;
+ QQmlJSScope::ConstPtr m_stringListType;
QQmlJSScope::ConstPtr m_urlType;
QQmlJSScope::ConstPtr m_dateTimeType;
QQmlJSScope::ConstPtr m_variantListType;
@@ -172,6 +208,27 @@ protected:
ParentMode m_parentMode = UseParentProperty;
QQmlJSLogger *m_logger = nullptr;
+
+ struct TrackedType
+ {
+ // The type originally found via type analysis.
+ QQmlJSScope::ConstPtr original;
+
+ // Any later replacement used to overwrite the contents of the clone.
+ QQmlJSScope::ConstPtr replacement;
+
+ // A clone of original, used to track the type,
+ // contents possibly overwritten by replacement.
+ QQmlJSScope::Ptr clone;
+ };
+
+ struct TypeTracker
+ {
+ QHash<QQmlJSScope::ConstPtr, QQmlJSScope::Ptr> listTypes;
+ QHash<QQmlJSScope::ConstPtr, TrackedType> trackedTypes;
+ };
+
+ std::unique_ptr<TypeTracker> m_typeTracker;
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp
index 81b69d1216..0b078b4868 100644
--- a/src/qmldebug/qqmldebugconnection.cpp
+++ b/src/qmldebug/qqmldebugconnection.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qeventloop.h>
#include <QtCore/qtimer.h>
+#include <QtCore/QHash>
#include <QtCore/qdatastream.h>
#include <QtNetwork/qlocalserver.h>
#include <QtNetwork/qlocalsocket.h>
diff --git a/src/qmldebug/qqmlenginecontrolclient_p_p.h b/src/qmldebug/qqmlenginecontrolclient_p_p.h
index 597ae23e3a..ec2cbd31f0 100644
--- a/src/qmldebug/qqmlenginecontrolclient_p_p.h
+++ b/src/qmldebug/qqmlenginecontrolclient_p_p.h
@@ -43,6 +43,8 @@
#include "qqmlenginecontrolclient_p.h"
#include "qqmldebugclient_p_p.h"
+#include <QtCore/QHash>
+
//
// W A R N I N G
// -------------
diff --git a/src/qmldom/qqmldomlinewriter_p.h b/src/qmldom/qqmldomlinewriter_p.h
index 2808282034..544a88fe70 100644
--- a/src/qmldom/qqmldomlinewriter_p.h
+++ b/src/qmldom/qqmldomlinewriter_p.h
@@ -55,6 +55,7 @@
#include <QtQml/private/qqmljssourcelocation_p.h>
#include <QtCore/QObject>
#include <QtCore/QAtomicInt>
+#include <QtCore/QMap>
#include <functional>
QT_BEGIN_NAMESPACE
diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp
index 3a946800d4..716da48bbf 100644
--- a/src/qmldom/qqmldomtypesreader.cpp
+++ b/src/qmldom/qqmldomtypesreader.cpp
@@ -174,7 +174,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
++it;
}
}
- comp.setFileName(jsScope->fileName());
+ comp.setFileName(jsScope->filePath());
comp.setName(jsScope->internalName());
m_currentPath = m_currentPath.key(comp.name())
.index(qmltypesFilePtr()->components().values(comp.name()).length());
diff --git a/src/qmlintegration/qqmlintegration.h b/src/qmlintegration/qqmlintegration.h
index d2d7b186ff..9aae01830b 100644
--- a/src/qmlintegration/qqmlintegration.h
+++ b/src/qmlintegration/qqmlintegration.h
@@ -50,6 +50,8 @@ namespace QQmlPrivate {
template<class> struct QmlAttachedAccessor;
template<class, class> struct QmlExtended;
template<typename, typename> struct QmlInterface;
+ template<class, class>
+ struct QmlExtendedNamespace;
}
template <typename T> class QList;
diff --git a/src/qmlmodels/CMakeLists.txt b/src/qmlmodels/CMakeLists.txt
index ccee022672..1f8ffcdadf 100644
--- a/src/qmlmodels/CMakeLists.txt
+++ b/src/qmlmodels/CMakeLists.txt
@@ -29,6 +29,11 @@ qt_internal_add_qml_module(QmlModels
GENERATE_PRIVATE_CPP_EXPORTS
)
+qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_itemmodel
+ SOURCES
+ qqmlmodelindexvaluetype.cpp qqmlmodelindexvaluetype_p.h
+)
+
qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_object_model
SOURCES
qqmlinstantiator.cpp qqmlinstantiator_p.h
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index 9ee9c91cd4..1463a84af9 100644
--- a/src/qmlmodels/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -953,7 +953,8 @@ QQmlAdaptorModel::Accessors::~Accessors()
}
QQmlAdaptorModel::QQmlAdaptorModel()
- : accessors(&qt_vdm_null_accessors)
+ : QQmlGuard<QObject>(QQmlAdaptorModel::objectDestroyedImpl, nullptr)
+ , accessors(&qt_vdm_null_accessors)
{
}
@@ -1046,9 +1047,10 @@ void QQmlAdaptorModel::useImportVersion(QTypeRevision revision)
modelItemRevision = revision;
}
-void QQmlAdaptorModel::objectDestroyed(QObject *)
+void QQmlAdaptorModel::objectDestroyedImpl(QQmlGuardImpl *guard)
{
- setModel(QVariant());
+ auto This = static_cast<QQmlAdaptorModel *>(guard);
+ This->setModel(QVariant());
}
QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
diff --git a/src/qmlmodels/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h
index 58991fbb15..06fe6068d7 100644
--- a/src/qmlmodels/qqmladaptormodel_p.h
+++ b/src/qmlmodels/qqmladaptormodel_p.h
@@ -170,8 +170,8 @@ public:
inline bool canFetchMore() const { return accessors->canFetchMore(*this); }
inline void fetchMore() { return accessors->fetchMore(*this); }
-protected:
- void objectDestroyed(QObject *) override;
+private:
+ static void objectDestroyedImpl(QQmlGuardImpl *);
};
class QQmlAdaptorModelProxyInterface
diff --git a/src/qml/types/qqmlmodelindexvaluetype.cpp b/src/qmlmodels/qqmlmodelindexvaluetype.cpp
index cbf2fef348..cbf2fef348 100644
--- a/src/qml/types/qqmlmodelindexvaluetype.cpp
+++ b/src/qmlmodels/qqmlmodelindexvaluetype.cpp
diff --git a/src/qml/types/qqmlmodelindexvaluetype_p.h b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
index 2c37d91c71..2c37d91c71 100644
--- a/src/qml/types/qqmlmodelindexvaluetype_p.h
+++ b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel.cpp b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
index 06a8c5c6d6..a2eac512ec 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel.cpp
+++ b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
@@ -517,6 +517,32 @@ void QQmlTreeModelToTableModel::expandRow(int n)
expandPendingRows();
}
+void QQmlTreeModelToTableModel::expandRecursively(int row, int depth)
+{
+ Q_ASSERT(depth == -1 || depth > 0);
+ const int startDepth = depthAtRow(row);
+
+ auto expandHelp = [=] (const auto expandHelp, const QModelIndex &index) -> void {
+ const int rowToExpand = itemIndex(index);
+ if (!m_expandedItems.contains(index))
+ expandRow(rowToExpand);
+
+ if (depth != -1 && depthAtRow(rowToExpand) == startDepth + depth - 1)
+ return;
+
+ const int childCount = m_model->rowCount(index);
+ for (int childRow = 0; childRow < childCount; ++childRow) {
+ const QModelIndex childIndex = m_model->index(childRow, 0, index);
+ if (m_model->hasChildren(childIndex))
+ expandHelp(expandHelp, childIndex);
+ }
+ };
+
+ const QModelIndex index = m_items[row].index;
+ if (index.isValid())
+ expandHelp(expandHelp, index);
+}
+
void QQmlTreeModelToTableModel::expandPendingRows(bool doInsertRows)
{
while (!m_itemsToExpand.isEmpty()) {
@@ -537,6 +563,30 @@ void QQmlTreeModelToTableModel::expandPendingRows(bool doInsertRows)
}
}
+void QQmlTreeModelToTableModel::collapseRecursively(int row)
+{
+ auto collapseHelp = [=] (const auto collapseHelp, const QModelIndex &index) -> void {
+ if (m_expandedItems.contains(index)) {
+ const int rowToCollapse = itemIndex(index);
+ if (rowToCollapse != -1)
+ collapseRow(rowToCollapse);
+ else
+ m_expandedItems.remove(index);
+ }
+
+ const int childCount = m_model->rowCount(index);
+ for (int childRow = 0; childRow < childCount; ++childRow) {
+ const QModelIndex childIndex = m_model->index(childRow, 0, index);
+ if (m_model->hasChildren(childIndex))
+ collapseHelp(collapseHelp, childIndex);
+ }
+ };
+
+ const QModelIndex index = m_items[row].index;
+ if (index.isValid())
+ collapseHelp(collapseHelp, index);
+}
+
void QQmlTreeModelToTableModel::collapseRow(int n)
{
if (!m_model || !isExpanded(n))
@@ -558,8 +608,13 @@ void QQmlTreeModelToTableModel::collapseRow(int n)
removeVisibleRows(n + 1, lastIndex);
}
-int QQmlTreeModelToTableModel::lastChildIndex(const QModelIndex &index)
+int QQmlTreeModelToTableModel::lastChildIndex(const QModelIndex &index) const
{
+ // The purpose of this function is to return the row of the last decendant of a node N.
+ // But note: index should point to the last child of N, and not N itself!
+ // This means that if index is not expanded, the last child will simply be index itself.
+ // Otherwise, since the tree underneath index can be of any depth, it will instead find
+ // the first sibling of N, get its table row, and simply return the row above.
if (!m_expandedItems.contains(index))
return itemIndex(index);
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
index a772b980e3..f86e135718 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
+++ b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
@@ -113,7 +113,7 @@ public:
int itemIndex(const QModelIndex &index) const;
void expandPendingRows(bool doInsertRows = true);
- int lastChildIndex(const QModelIndex &index);
+ int lastChildIndex(const QModelIndex &index) const;
void removeVisibleRows(int startIndex, int endIndex, bool doRemoveRows = true);
void dump() const;
@@ -137,7 +137,9 @@ public slots:
bool hasSiblings(int row) const;
int depthAtRow(int row) const;
void expandRow(int n);
+ void expandRecursively(int row, int depth);
void collapseRow(int n);
+ void collapseRecursively(int row);
private slots:
void modelHasBeenDestroyed();
diff --git a/src/qmlmodels/qquickpackage.cpp b/src/qmlmodels/qquickpackage.cpp
index bf83afb329..999919562b 100644
--- a/src/qmlmodels/qquickpackage.cpp
+++ b/src/qmlmodels/qquickpackage.cpp
@@ -90,11 +90,14 @@ public:
struct DataGuard : public QQmlGuard<QObject>
{
- DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; }
+ DataGuard(QObject *obj, QList<DataGuard> *l) : QQmlGuard<QObject>(DataGuard::objectDestroyedImpl, nullptr), list(l) { (QQmlGuard<QObject>&)*this = obj; }
QList<DataGuard> *list;
- void objectDestroyed(QObject *) override {
+
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) {
+ auto This = static_cast<DataGuard *>(guard);
// we assume priv will always be destroyed after objectDestroyed calls
- list->removeOne(*this);
+ This->list->removeOne(*This);
}
};
diff --git a/src/qmltest/CMakeLists.txt b/src/qmltest/CMakeLists.txt
index 0a3dd4abd1..7f55ebc8b0 100644
--- a/src/qmltest/CMakeLists.txt
+++ b/src/qmltest/CMakeLists.txt
@@ -33,12 +33,12 @@ qt_internal_add_qml_module(QuickTest
Qt::CorePrivate
Qt::Gui
Qt::QmlPrivate
- Qt::Quick
Qt::QuickPrivate
Qt::TestPrivate
PUBLIC_LIBRARIES
Qt::Core
Qt::Test
+ Qt::Quick
PRIVATE_MODULE_INTERFACE
Qt::TestPrivate
)
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index aa81b0e180..4c8289dd22 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -436,7 +436,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
testPath = s;
}
-#if defined(Q_OS_ANDROID)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_INTEGRITY)
if (testPath.isEmpty())
testPath = QLatin1String(":/");
#endif
diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h
index 58b53d7b6b..83e604688c 100644
--- a/src/qmltest/quicktestresult_p.h
+++ b/src/qmltest/quicktestresult_p.h
@@ -143,7 +143,7 @@ public Q_SLOTS:
void warn(const QString &message, const QUrl &location, int line);
void ignoreWarning(const QJSValue &message);
- void failOnWarning(const QJSValue &message);
+ Q_REVISION(6, 3) void failOnWarning(const QJSValue &message);
void wait(int ms);
void sleep(int ms);
diff --git a/src/qmltest/quicktestutil.cpp b/src/qmltest/quicktestutil.cpp
index 25863c3842..fd6f294673 100644
--- a/src/qmltest/quicktestutil.cpp
+++ b/src/qmltest/quicktestutil.cpp
@@ -72,6 +72,8 @@ void QuickTestUtil::populateClipboardText(int lineCount)
for (int i = lineCount; i > 0; --i)
lines << fmt.arg(i).arg(i - 1);
QGuiApplication::clipboard()->setText(lines.join(u'\n'));
+#else
+ Q_UNUSED(lineCount)
#endif
}
diff --git a/src/qmltyperegistrar/qmltyperegistrar.cpp b/src/qmltyperegistrar/qmltyperegistrar.cpp
index 8b16340342..e5d1bb95d6 100644
--- a/src/qmltyperegistrar/qmltyperegistrar.cpp
+++ b/src/qmltyperegistrar/qmltyperegistrar.cpp
@@ -256,11 +256,20 @@ int main(int argc, char **argv)
fprintf(output, "\n\n");
+ // Keep this in sync with _qt_internal_get_escaped_uri in CMake
QString moduleAsSymbol = module;
- moduleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
+ moduleAsSymbol.replace(QRegularExpression(QStringLiteral("[^A-Za-z0-9]")), QStringLiteral("_"));
- const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
+ QString underscoredModuleAsSymbol = module;
+ underscoredModuleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
+
+ if (underscoredModuleAsSymbol != moduleAsSymbol
+ || underscoredModuleAsSymbol.isEmpty()
+ || underscoredModuleAsSymbol.front().isDigit()) {
+ qWarning() << module << "is an invalid QML module URI. You cannot import this.";
+ }
+ const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
fprintf(output,
"#if !defined(QT_STATIC)\n"
"#define Q_QMLTYPE_EXPORT Q_DECL_EXPORT\n"
@@ -319,8 +328,8 @@ int main(int argc, char **argv)
QString targetName = className;
QString extendedName;
bool seenQmlElement = false;
- QJsonArray classInfos = classDef.value(QLatin1String("classInfos")).toArray();
- for (const QJsonValueRef v : classInfos) {
+ const QJsonArray classInfos = classDef.value(QLatin1String("classInfos")).toArray();
+ for (const QJsonValueConstRef v : classInfos) {
const QString name = v[QStringLiteral("name")].toString();
if (name == QStringLiteral("QML.Element"))
seenQmlElement = true;
diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp
index 559b4c71d2..fdb6a220df 100644
--- a/src/qmltyperegistrar/qmltypescreator.cpp
+++ b/src/qmltyperegistrar/qmltypescreator.cpp
@@ -85,7 +85,23 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
m_qml.writeArrayBinding(QLatin1String("interfaces"), interfaces);
}
- if (collector.elementName.isEmpty())
+ if (!collector.deferredNames.isEmpty()) {
+ QStringList deferredNames;
+ for (const QString &name : collector.deferredNames)
+ deferredNames << enquote(name);
+
+ m_qml.writeArrayBinding(QLatin1String("deferredNames"), deferredNames);
+ }
+
+ if (!collector.immediateNames.isEmpty()) {
+ QStringList immediateNames;
+ for (const QString &name : collector.immediateNames)
+ immediateNames << enquote(name);
+
+ m_qml.writeArrayBinding(QLatin1String("immediateNames"), immediateNames);
+ }
+
+ if (collector.elementName.isEmpty()) // e.g. if QML_ANONYMOUS
return;
if (!collector.sequenceValueType.isEmpty()) {
@@ -125,22 +141,6 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (collector.hasCustomParser)
m_qml.writeScriptBinding(QLatin1String("hasCustomParser"), QLatin1String("true"));
- if (!collector.deferredNames.isEmpty()) {
- QStringList deferredNames;
- for (const QString &name : collector.deferredNames)
- deferredNames << enquote(name);
-
- m_qml.writeArrayBinding(QLatin1String("deferredNames"), deferredNames);
- }
-
- if (!collector.immediateNames.isEmpty()) {
- QStringList immediateNames;
- for (const QString &name : collector.immediateNames)
- immediateNames << enquote(name);
-
- m_qml.writeArrayBinding(QLatin1String("immediateNames"), immediateNames);
- }
-
m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects);
if (!collector.attachedType.isEmpty())
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 0f8733eb0f..a757ec9afa 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -509,6 +509,10 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_wheelevent
handlers/qquickwheelhandler_p_p.h
)
+qt_internal_extend_target(Quick CONDITION QT_FEATURE_im
+ SOURCES
+ util/qquickinputmethod.cpp util/qquickinputmethod_p.h
+)
qt_internal_create_tracepoints(Quick qtquick.tracepoints)
qt_internal_add_docs(Quick
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 933a5b5cbe..9a4ce960f1 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -448,6 +448,11 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const
return accessibleUnignoredChildren(item());
}
+static bool isTextRole(QAccessible::Role role)
+{
+ return role == QAccessible::EditableText || role == QAccessible::StaticText;
+}
+
QAccessible::State QAccessibleQuickItem::state() const
{
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
@@ -465,7 +470,7 @@ QAccessible::State QAccessibleQuickItem::state() const
state.offscreen = true;
if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && object()->property("checked").toBool())
state.checked = true;
- if (item()->activeFocusOnTab() || role() == QAccessible::EditableText)
+ if (item()->activeFocusOnTab() || isTextRole(role()))
state.focusable = true;
if (item()->hasActiveFocus())
state.focused = true;
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
index 15b8394ebf..cbbaa36c7b 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
@@ -30,17 +30,17 @@
\title Qt Quick Layouts
\brief A module with a set of QML elements that arrange QML items in a user interface.
- Qt Quick Layouts are a set of QML types used to arrange items in a user interface. In contrast
- to \l{Item Positioners}{positioners}, Qt Quick Layouts can also resize their items. This makes
- them well suited for resizable user interfaces. Since layouts are items they can consequently
- be nested.
+ Qt Quick Layouts are a set of QML types used to arrange items in a user
+ interface. In contrast to \l{Item Positioners}{positioners}, Qt Quick
+ Layouts can also resize their child items. This makes them well suited for
+ resizable user interfaces.
Visit the \l{Qt Quick Layouts Overview} page to get started.
\section1 Using the Module
- The QML types can be imported into your application by adding the following
- import statement in your \c {.qml} file.
+ The \l {Qt Quick Layouts QML Types}{QML types} can be imported into your
+ application by adding the following import statement in your \c {.qml} file.
\qml
import QtQuick.Layouts
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc
index 0df832cca9..0df832cca9 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index eab3b041bb..f0c7b7cbf2 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -185,6 +185,7 @@ force the usage of a given loop. To verify which render loop is in use, enable
the \c qt.scenegraph.general \l {QLoggingCategory}{logging category}.
\section2 Threaded Render Loop ('threaded')
+\target threaded_render_loop
On many configurations, the scene graph rendering will happen on a
dedicated render thread. This is done to increase parallelism of
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 596a061c0d..5c35283a9b 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -80,7 +80,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype color
+ \qmlvaluetype color
\ingroup qtquickvaluetypes
\brief an ARGB color value.
\target colorvaluetypedocs
@@ -140,7 +140,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype font
+ \qmlvaluetype font
\ingroup qtquickvaluetypes
\brief a font value with the properties of QFont.
\target fontvaluetypedocs
@@ -256,7 +256,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype vector2d
+ \qmlvaluetype vector2d
\ingroup qtquickvaluetypes
\brief A vector2d type has x and y attributes.
@@ -384,7 +384,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype vector3d
+ \qmlvaluetype vector3d
\ingroup qtquickvaluetypes
\brief a value with x, y, and z attributes.
@@ -555,7 +555,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype vector4d
+ \qmlvaluetype vector4d
\ingroup qtquickvaluetypes
\brief A vector4d type has x, y, z and w attributes.
@@ -696,7 +696,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype quaternion
+ \qmlvaluetype quaternion
\ingroup qtquickvaluetypes
\brief A quaternion type has scalar, x, y, and z attributes.
@@ -856,7 +856,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype matrix4x4
+ \qmlvaluetype matrix4x4
\ingroup qtquickvaluetypes
\brief A matrix4x4 type is a 4-row and 4-column matrix
diff --git a/src/quick/doc/src/qt6-changes.qdoc b/src/quick/doc/src/qt6-changes.qdoc
index 58a7ebb3e1..defacd95a1 100644
--- a/src/quick/doc/src/qt6-changes.qdoc
+++ b/src/quick/doc/src/qt6-changes.qdoc
@@ -138,7 +138,7 @@
OpenGL. It will not be functional when using another graphics API, such as
Vulkan or Metal. Applications relying on QQuickWidget should force the usage
of OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\section2 Changes to QQuick* APIs
@@ -206,7 +206,7 @@
not be functional when using another graphics API, such as Vulkan or Metal.
Applications relying on QQuickFramebufferObject should force the usage of
OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\li QQuickRenderControl has a slightly changed API: grab() is now
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index a8665a3641..2e070fa8df 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -38,26 +38,25 @@ provides a visual canvas and includes types for creating and animating
visual components, receiving user input, creating data models and views
and delayed object instantiation.
-The Qt Quick module provides both a \l{Qt Quick QML Types}{QML API} which supplies
-QML types for creating user interfaces with the QML language, and a
+The Qt Quick module provides both a \l{Qt Quick QML Types}{QML API}, which
+supplies QML types for creating user interfaces with the QML language, and a
\l{Qt Quick C++ Classes}{C++ API} for extending QML applications with C++ code.
\note A set of Qt Quick-based UI controls is also available to create user
interfaces. See \l{Qt Quick Controls} for more information.
-For those new to QML and Qt Quick, please see
-\l{QML Applications}
-for an introduction to writing QML applications.
+If you're new to QML and Qt Quick, please see \l{QML Applications} for an
+introduction to writing QML applications.
\section1 Using the Module
\section2 QML API
-The QML types in Qt Quick are available through the \c QtQuick import. To use the
-types, add the following import statement to your .qml file:
+The QML types in Qt Quick are available through the \c QtQuick import. To use
+the types, add the following import statement to your .qml file:
\qml
-import QtQml
+import QtQuick
\endqml
\section2 C++ API
@@ -87,13 +86,13 @@ QT += quick
\section1 Important Concepts in Qt Quick
-Qt Quick provides everything needed to create a rich application with a fluid
-and dynamic user interface. It enables user interfaces to be built around the
+Qt Quick provides everything you need to create a rich application with a fluid
+and dynamic user interface. It enables you to build user interfaces around the
behavior of user interface components and how they connect with one another,
and it provides a visual canvas with its own coordinate system and rendering
-engine. Animation and transition effects are a first class concept in Qt Quick,
-and visual effects can be supplemented through specialized components for
-particle and shader effects.
+engine. Animation and transition effects are first class concepts in Qt Quick,
+and you can add visual effects through specialized components for particle and
+shader effects.
\list
\li \l{Important Concepts In Qt Quick - The Visual Canvas}{The Visual Canvas}
@@ -105,7 +104,7 @@ particle and shader effects.
\li \l{Important Concepts In Qt Quick - Convenience Types}{Convenience Types}
\endlist
-When using the \c QtQuick module, you will need to know how to write QML
+When using the Qt Quick module, you will need to know how to write QML
applications using the QML language. In particular, QML Basics and QML
Essentials from the \l{QML Applications} page.
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h
index ffb01563ed..dcfa863553 100644
--- a/src/quick/items/context2d/qquickcanvascontext_p.h
+++ b/src/quick/items/context2d/qquickcanvascontext_p.h
@@ -56,10 +56,14 @@
QT_REQUIRE_CONFIG(quick_canvas);
#include <QtQuick/qquickitem.h>
-#include <QtQml/private/qv4value_p.h>
+#include <QtQml/private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
+namespace QV4 {
+ struct ExecutionEngine;
+}
+
class QQuickCanvasItem;
class QSGLayer;
diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index 1972a9a507..11e1ec3fb2 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -67,6 +67,7 @@ class QQuickCanvasContext;
class QQuickCanvasItemPrivate;
class QQuickPixmap;
+class QQmlV4Function;
class QQuickCanvasPixmap : public QQmlRefCount
{
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index ea1354725f..d769c4396b 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(quick_canvas);
#include <QtCore/qqueue.h>
#include <QtCore/QWaitCondition>
-#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
//#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose!
@@ -78,6 +77,10 @@ QT_REQUIRE_CONFIG(quick_canvas);
QT_BEGIN_NAMESPACE
+namespace QV4 {
+ struct ExecutionEngine;
+}
+
class QQuickContext2DCommandBuffer;
class QQuickContext2DTexture;
class QQuickPixmap;
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 0b593c0089..730bbe4404 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -406,9 +406,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role)
m_state.focusable = true;
break;
case QAccessible::StaticText:
- if (!m_stateExplicitlySet.readOnly) {
+ if (!m_stateExplicitlySet.readOnly)
m_state.readOnly = true;
- }
+ if (!m_stateExplicitlySet.focusable)
+ m_state.focusable = true;
break;
default:
break;
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index e17a28d07e..a84af80ee9 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -73,11 +73,11 @@ class QQuickDragGrabber
class Item : public QQmlGuard<QQuickItem>
{
public:
- Item(QQuickItem *item) : QQmlGuard<QQuickItem>(item) {}
+ Item(QQuickItem *item) : QQmlGuard<QQuickItem>(Item::objectDestroyedImpl, item) {}
QIntrusiveListNode node;
- protected:
- void objectDestroyed(QQuickItem *) override { delete this; }
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) { delete static_cast<Item *>(guard); }
};
typedef QIntrusiveList<Item, &Item::node> ItemList;
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 41f682fe57..3cf91cdc8f 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -51,8 +51,8 @@
// We mean it.
//
-#include "qquickpainteditem.h"
#include <private/qtquickglobal_p.h>
+#include <QtQuick/qquickitem.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 197d891b2a..2fdb0f00c8 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -477,6 +477,7 @@ private:
friend class QSGRenderer;
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
+ friend class QQuickAnchorChanges;
Q_DISABLE_COPY(QQuickItem)
Q_DECLARE_PRIVATE(QQuickItem)
};
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index bde39d99bd..ea1aa7b0c9 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -1041,9 +1041,15 @@ void QQuickLoaderPrivate::createComponent()
const QQmlComponent::CompilationMode mode = asynchronous
? QQmlComponent::Asynchronous
: QQmlComponent::PreferSynchronous;
- QQmlContext *context = qmlContext(q);
- component.setObject(new QQmlComponent(
- context->engine(), context->resolvedUrl(source), mode, q), q);
+ if (QQmlContext *context = qmlContext(q)) {
+ if (QQmlEngine *engine = context->engine()) {
+ component.setObject(new QQmlComponent(
+ engine, context->resolvedUrl(source), mode, q), q);
+ return;
+ }
+ }
+
+ qmlWarning(q) << "createComponent: Cannot find a QML engine.";
}
#include <moc_qquickloader_p.cpp>
diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h
index 1d60f2845f..c6216ccc7c 100644
--- a/src/quick/items/qquickloader_p.h
+++ b/src/quick/items/qquickloader_p.h
@@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
+class QQmlV4Function;
class Q_QUICK_PRIVATE_EXPORT QQuickLoader : public QQuickImplicitSizeItem
{
Q_OBJECT
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index b178803c7d..dac9e2cb59 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -56,12 +56,14 @@
#include "qquickitemchangelistener_p.h"
#include <qqmlincubator.h>
-#include <private/qv4value_p.h>
+#include <private/qv4staticvalue_p.h>
+#include <private/qv4persistent_p.h>
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
+class QQmlV4Function;
class QQuickLoaderIncubator : public QQmlIncubator
{
public:
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 1f690f1e85..5d29408b06 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -331,6 +331,7 @@ bool QQuickRenderControl::initialize()
params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
params.maybeSurface = d->window;
renderContext->initialize(&params);
+ d->initialized = true;
} else {
qWarning("QRhi is only compatible with default adaptation");
return false;
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 38e46b10d4..8f16940ea3 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -81,7 +81,7 @@ public:
QQuickWindow *window() const;
protected:
- QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
+ explicit QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
Q_SIGNALS:
void renderRequested();
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index 54517e1ed6..719c682769 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -482,12 +482,12 @@ void QQuickParentChangePrivate::reverseRewindHelper(const std::unique_ptr<QQuick
{
if (!target || !snapshot)
return;
- auto targetPriv = QQuickItemPrivate::get(target);
+
// leave existing bindings alive; new bindings are applied in applyBindings
- targetPriv->x.setValueBypassingBindings(snapshot->x);
- targetPriv->y.setValueBypassingBindings(snapshot->y);
- targetPriv->width.setValueBypassingBindings(snapshot->width);
- targetPriv->height.setValueBypassingBindings(snapshot->height);
+ // setPosition and setSize update the geometry without invalidating bindings
+ target->setPosition(QPointF(snapshot->x, snapshot->y));
+ target->setSize(QSizeF(snapshot->width, snapshot->height));
+
target->setScale(snapshot->scale);
target->setRotation(snapshot->rotation);
target->setParentItem(snapshot->parent);
@@ -1094,6 +1094,7 @@ void QQuickAnchorChanges::reverse()
QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask;
QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask;
+ const QRectF oldGeometry(d->target->position(), d->target->size());
bool stateSetWidth = (stateHAnchors &&
stateHAnchors != QQuickAnchors::LeftAnchor &&
stateHAnchors != QQuickAnchors::RightAnchor &&
@@ -1102,8 +1103,11 @@ void QQuickAnchorChanges::reverse()
origHAnchors != QQuickAnchors::LeftAnchor &&
origHAnchors != QQuickAnchors::RightAnchor &&
origHAnchors != QQuickAnchors::HCenterAnchor);
- if (d->origWidth.isValid() && stateSetWidth && !origSetWidth)
- d->target->setWidth(d->origWidth.value);
+ if (d->origWidth.isValid() && stateSetWidth && !origSetWidth && !qt_is_nan(d->origWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (targetPrivate->width != d->origWidth)
+ targetPrivate->width.setValueBypassingBindings(d->origWidth);
+ }
bool stateSetHeight = (stateVAnchors &&
stateVAnchors != QQuickAnchors::TopAnchor &&
@@ -1115,14 +1119,23 @@ void QQuickAnchorChanges::reverse()
origVAnchors != QQuickAnchors::BottomAnchor &&
origVAnchors != QQuickAnchors::VCenterAnchor &&
origVAnchors != QQuickAnchors::BaselineAnchor);
- if (d->origHeight.isValid() && stateSetHeight && !origSetHeight)
- d->target->setHeight(d->origHeight.value);
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (targetPrivate->height != d->origHeight)
+ targetPrivate->height.setValueBypassingBindings(d->origHeight);
+ }
- if (stateHAnchors && !origHAnchors)
- d->target->setX(d->origX);
+ if (stateHAnchors && !origHAnchors && !qt_is_nan(d->origX) && d->origX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->origX);
- if (stateVAnchors && !origVAnchors)
- d->target->setY(d->origY);
+ if (stateVAnchors && !origVAnchors && !qt_is_nan(d->origY) && d->origY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->origY);
+
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
+ }
}
QQuickStateActionEvent::EventType QQuickAnchorChanges::type() const
@@ -1316,15 +1329,31 @@ void QQuickAnchorChanges::rewind()
return;
QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target);
+ const QRectF oldGeometry(d->target->position(), d->target->size());
+
+ // Restore previous values (but not previous bindings, i.e. anchors).
+ // Also, don't drop any new bindings.
+ if (!qt_is_nan(d->rewindX) && d->rewindX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->rewindX);
+ if (!qt_is_nan(d->rewindY) && d->rewindY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->rewindY);
+
+ if (targetPrivate->widthValid() && !qt_is_nan(d->rewindWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (d->rewindWidth != targetPrivate->width)
+ targetPrivate->width.setValueBypassingBindings(d->rewindWidth);
+ }
- //restore previous values (but not previous bindings, i.e. anchors)
- d->target->setX(d->rewindX);
- d->target->setY(d->rewindY);
- if (targetPrivate->widthValid()) {
- d->target->setWidth(d->rewindWidth);
+ if (targetPrivate->heightValid() && !qt_is_nan(d->rewindHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (d->rewindHeight != targetPrivate->height)
+ targetPrivate->height.setValueBypassingBindings(d->rewindHeight);
}
- if (targetPrivate->heightValid()) {
- d->target->setHeight(d->rewindHeight);
+
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
}
}
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 4ab8c954d1..adde42321c 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -556,8 +556,8 @@
/*!
\qmlmethod Point QtQuick::TableView::cellAtPos(point position, bool includeSpacing)
- Returns the cell at the given \a position in the view. If no cell intersects with
- \a position, the return value will be \c point(-1, -1).
+ Returns the cell at the given \a position in the view. If no \l {isRowLoaded()}{loaded}
+ cell intersects with \a position, the return value will be \c point(-1, -1).
If \a includeSpacing is set to \c true, a cell's bounding box will be considered
to include half the adjacent \l rowSpacing and \l columnSpacing on each side. The
@@ -643,6 +643,66 @@
*/
/*!
+ \qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column)
+ \since 6.4
+
+ Returns the \l QModelIndex that maps to \a row and \a column in the view.
+
+ \a row and \a column should be the row and column in the view (table row and
+ table column), and not a row and column in the model.
+
+ \sa rowAtIndex(), columnAtIndex()
+*/
+
+/*!
+ \qmlmethod QModelIndex QtQuick::TableView::modelIndex(point cell)
+ \since 6.4
+
+ Convenience function for doing:
+ \code
+ modelIndex(cell.y, cell.x)
+ \endcode
+
+ A cell is simply a \l point that combines row and column into
+ a single type. Note that \c point.x will map to the column, and
+ \c point.y will map to the row.
+*/
+
+/*!
+ \qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the row in the view that maps to \a modelIndex in the model.
+
+ \sa columnAtIndex(), modelIndex()
+*/
+
+/*!
+ \qmlmethod int QtQuick::TableView::columnAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the column in the view that maps to \a modelIndex in the model.
+
+ \sa rowAtIndex(), modelIndex()
+*/
+
+/*!
+ \qmlmethod point QtQuick::TableView::cellAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the cell in the view that maps to \a modelIndex in the model.
+ Convenience function for doing:
+
+ \code
+ Qt.point(columnAtIndex(modelIndex), rowAtIndex(modelIndex))
+ \endcode
+
+ A cell is simply a \l point that combines row and column into
+ a single type. Note that \c point.x will map to the column, and
+ \c point.y will map to the row.
+*/
+
+/*!
\qmlattachedproperty TableView QtQuick::TableView::view
This attached property holds the view that manages the delegate instance.
@@ -688,10 +748,9 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
-static const int kEdgeIndexNotSet = -2;
-static const int kEdgeIndexAtEnd = -3;
-static const char* kRequiredProperty = "_qt_isrequiredpropery_selected";
+static const char* kRequiredProperties = "_qt_tableview_requiredpropertymask";
+static const char* kRequiredProperty_selected = "selected";
const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0);
const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0);
@@ -782,6 +841,40 @@ void QQuickTableViewPrivate::dumpTable() const
qWarning() << "Window capture saved to:" << path;
}
+void QQuickTableViewPrivate::setRequiredProperty(const char *property,
+ const QVariant &value, int serializedModelIndex, QObject *object, bool init)
+{
+ if (!qobject_cast<QQmlTableInstanceModel *>(model)) {
+ // TableView only supports using required properties when backed by
+ // a QQmlTableInstanceModel. This is almost always the case, except
+ // if you assign it an ObjectModel or a DelegateModel (which are really
+ // not supported by TableView, it expects a QAIM).
+ return;
+ }
+
+ // Attaching a property list to the delegate item is just a
+ // work-around until QMetaProperty::isRequired() works (QTBUG-98846).
+ const QString propertyName = QString::fromUtf8(property);
+
+ if (init) {
+ const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value);
+ if (wasRequired) {
+ QStringList propertyList = object->property(kRequiredProperties).toStringList();
+ object->setProperty(kRequiredProperties, propertyList << propertyName);
+ }
+ } else {
+ const QStringList propertyList = object->property(kRequiredProperties).toStringList();
+ if (!propertyList.contains(propertyName)) {
+ // We only write to properties that are required
+ return;
+ }
+ const auto metaObject = object->metaObject();
+ const int propertyIndex = metaObject->indexOfProperty(property);
+ const auto metaProperty = metaObject->property(propertyIndex);
+ metaProperty.write(object, value);
+ }
+}
+
QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const
{
return const_cast<QQuickTableView *>(q_func())->contentItem();
@@ -993,7 +1086,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
const bool outsideBottom = pos.y() >= viewportRect.bottom() - 1;
if (outsideLeft) {
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
+ const bool firstColumnLoaded = atTableEnd(Qt::LeftEdge);
const qreal remainingDist = viewportRect.left() - loadedTableOuterRect.left();
if (remainingDist > 0 || !firstColumnLoaded) {
qreal stepX = step.width();
@@ -1003,7 +1096,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
dist.setWidth(pos.x() - viewportRect.left() - 1);
}
} else if (outsideRight) {
- const bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd;
+ const bool lastColumnLoaded = atTableEnd(Qt::RightEdge);
const qreal remainingDist = loadedTableOuterRect.right() - viewportRect.right();
if (remainingDist > 0 || !lastColumnLoaded) {
qreal stepX = step.width();
@@ -1015,7 +1108,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
}
if (outsideTop) {
- const bool firstRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
+ const bool firstRowLoaded = atTableEnd(Qt::TopEdge);
const qreal remainingDist = viewportRect.top() - loadedTableOuterRect.top();
if (remainingDist > 0 || !firstRowLoaded) {
qreal stepY = step.height();
@@ -1025,7 +1118,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
dist.setHeight(pos.y() - viewportRect.top() - 1);
}
} else if (outsideBottom) {
- const bool lastRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd;
+ const bool lastRowLoaded = atTableEnd(Qt::BottomEdge);
const qreal remainingDist = loadedTableOuterRect.bottom() - viewportRect.bottom();
if (remainingDist > 0 || !lastRowLoaded) {
qreal stepY = step.height();
@@ -1081,14 +1174,13 @@ int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex)
{
// Convert QModelIndex to cell index. A cell index is just an
// integer representation of a cell instead of using a QPoint.
- if (modelIndex.parent().isValid()) {
- // TableView only uses the root items of the model
+ const QPoint cell = q_func()->cellAtIndex(modelIndex);
+ if (!cellIsValid(cell))
return -1;
- }
- return modelIndexAtCell(QPoint(modelIndex.column(), modelIndex.row()));
+ return modelIndexAtCell(cell);
}
-int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge)
+int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) const
{
return int(log2(float(edge)));
}
@@ -1102,7 +1194,7 @@ void QQuickTableViewPrivate::clearEdgeSizeCache()
cachedNextVisibleEdgeIndex[edgeToArrayIndex(edge)].startIndex = kEdgeIndexNotSet;
}
-int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge)
+int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const
{
// Find the next column (or row) around the loaded table that is
// visible, and should be loaded next if the content item moves.
@@ -1117,7 +1209,7 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge)
return nextVisibleEdgeIndex(edge, startIndex);
}
-int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex)
+int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const
{
// First check if we have already searched for the first visible index
// after the given startIndex recently, and if so, return the cached result.
@@ -1200,28 +1292,6 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex)
return foundIndex;
}
-bool QQuickTableViewPrivate::allColumnsLoaded()
-{
- // Returns true if all the columns in the model (that are not
- // hidden by the columnWidthProvider) are currently loaded and visible.
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
- if (!firstColumnLoaded)
- return false;
- bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd;
- return lastColumnLoaded;
-}
-
-bool QQuickTableViewPrivate::allRowsLoaded()
-{
- // Returns true if all the rows in the model (that are not hidden
- // by the columnWidthProvider) are currently loaded and visible.
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
- if (!firstColumnLoaded)
- return false;
- bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd;
- return lastColumnLoaded;
-}
-
void QQuickTableViewPrivate::updateContentWidth()
{
// Note that we actually never really know what the content size / size of the full table will
@@ -1941,6 +2011,13 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column)
return columnWidth;
}
+qreal QQuickTableViewPrivate::getEffectiveRowY(int row) const
+{
+ // Return y pos of row after layout
+ Q_TABLEVIEW_ASSERT(loadedRows.contains(row), row);
+ return loadedTableItem(QPoint(leftColumn(), row))->geometry().y();
+}
+
qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const
{
// Return row height after layout
@@ -1948,6 +2025,13 @@ qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const
return loadedTableItem(QPoint(leftColumn(), row))->geometry().height();
}
+qreal QQuickTableViewPrivate::getEffectiveColumnX(int column) const
+{
+ // Return x pos of column after layout
+ Q_TABLEVIEW_ASSERT(loadedColumns.contains(column), column);
+ return loadedTableItem(QPoint(column, topRow()))->geometry().x();
+}
+
qreal QQuickTableViewPrivate::getEffectiveColumnWidth(int column) const
{
// Return column width after layout
@@ -1989,7 +2073,7 @@ qreal QQuickTableViewPrivate::getRowLayoutHeight(int row)
return rowHeight;
}
-qreal QQuickTableViewPrivate::getColumnWidth(int column)
+qreal QQuickTableViewPrivate::getColumnWidth(int column) const
{
// Return the width of the given column, if explicitly set. Return 0 if the column
// is hidden, and -1 if the width is not set (which means that the width should
@@ -2030,7 +2114,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column)
return columnWidth;
}
-qreal QQuickTableViewPrivate::getRowHeight(int row)
+qreal QQuickTableViewPrivate::getRowHeight(int row) const
{
// Return the height of the given row, if explicitly set. Return 0 if the row
// is hidden, and -1 if the height is not set (which means that the height should
@@ -2071,14 +2155,14 @@ qreal QQuickTableViewPrivate::getRowHeight(int row)
return rowHeight;
}
-bool QQuickTableViewPrivate::isColumnHidden(int column)
+bool QQuickTableViewPrivate::isColumnHidden(int column) const
{
// A column is hidden if the width is explicit set to zero (either by
// using a columnWidthProvider, or by overriding getColumnWidth()).
return qFuzzyIsNull(getColumnWidth(column));
}
-bool QQuickTableViewPrivate::isRowHidden(int row)
+bool QQuickTableViewPrivate::isRowHidden(int row) const
{
// A row is hidden if the height is explicit set to zero (either by
// using a rowHeightProvider, or by overriding getRowHeight()).
@@ -2336,18 +2420,25 @@ void QQuickTableViewPrivate::processRebuildTable()
return;
}
+ if (rebuildState == RebuildState::CancelOvershoot) {
+ cancelOvershootAfterLayout();
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
const bool preload = (rebuildOptions & RebuildOption::All
&& reusableFlag == QQmlTableInstanceModel::Reusable);
if (rebuildState == RebuildState::PreloadColumns) {
- if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) != kEdgeIndexAtEnd)
+ if (preload && !atTableEnd(Qt::RightEdge))
loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
if (!moveToNextRebuildState())
return;
}
if (rebuildState == RebuildState::PreloadRows) {
- if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) != kEdgeIndexAtEnd)
+ if (preload && !atTableEnd(Qt::BottomEdge))
loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
if (!moveToNextRebuildState())
return;
@@ -2585,12 +2676,14 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
relayoutTableItems();
syncLoadedTableRectFromLoadedTable();
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) {
+ const bool allColumnsLoaded = atTableEnd(Qt::LeftEdge) && atTableEnd(Qt::RightEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded) {
updateAverageColumnWidth();
updateContentWidth();
}
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) {
+ const bool allRowsLoaded = atTableEnd(Qt::TopEdge) && atTableEnd(Qt::BottomEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded) {
updateAverageRowHeight();
updateContentHeight();
}
@@ -2670,6 +2763,32 @@ void QQuickTableViewPrivate::adjustViewportYAccordingToAlignment()
syncViewportRect();
}
+void QQuickTableViewPrivate::cancelOvershootAfterLayout()
+{
+ Q_Q(QQuickTableView);
+
+ // Note: we only want to cancel overshoot from a rebuild if we're supposed to position
+ // the view on a specific cell. The app is allowed to overshoot by setting contentX and
+ // contentY manually. Also, if this view is a sync child, we should always stay in sync
+ // with the syncView, so then we don't do anything.
+ const bool positionVertically = rebuildOptions.testFlag(RebuildOption::PositionViewAtRow);
+ const bool positionHorizontally = rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn);
+ const bool cancelVertically = positionVertically && !syncVertically;
+ const bool cancelHorizontally = positionHorizontally && !syncHorizontally;
+
+ if (cancelHorizontally && !qFuzzyIsNull(q->horizontalOvershoot())) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot horizontally:" << q->horizontalOvershoot();
+ setLocalViewportX(q->horizontalOvershoot() < 0 ? -q->minXExtent() : -q->maxXExtent());
+ syncViewportRect();
+ }
+
+ if (cancelVertically && !qFuzzyIsNull(q->verticalOvershoot())) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot vertically:" << q->verticalOvershoot();
+ setLocalViewportY(q->verticalOvershoot() < 0 ? -q->minYExtent() : -q->maxYExtent());
+ syncViewportRect();
+ }
+}
+
void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
{
Q_Q(QQuickTableView);
@@ -2728,7 +2847,7 @@ void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMo
processLoadRequest();
}
-void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
+void QQuickTableViewPrivate::loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode)
{
// Unload table edges that have been moved outside the visible part of the
// table (including buffer area), and load new edges that has been moved inside.
@@ -2765,7 +2884,7 @@ void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) {
tableModified = true;
- loadEdge(edge, QQmlIncubator::AsynchronousIfNested);
+ loadEdge(edge, incubationMode);
if (loadRequest.isActive())
return;
}
@@ -2970,11 +3089,10 @@ bool QQuickTableViewPrivate::selectedInSelectionModel(const QPoint &cell) const
if (!model)
return false;
- const QModelIndex modelIndex = model->index(cell.y(), cell.x());
- return selectionModel->isSelected(modelIndex);
+ return selectionModel->isSelected(q_func()->modelIndex(cell));
}
-void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const
+void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
const auto &selectedIndexes = selected.indexes();
const auto &deselectedIndexes = deselected.indexes();
@@ -2984,40 +3102,25 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti
setSelectedOnDelegateItem(deselectedIndexes.at(i), false);
}
-void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems() const
-{
- for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) {
- const QPoint cell = cellAtModelIndex(*it);
- const bool selected = selectedInSelectionModel(cell);
- setSelectedOnDelegateItem(loadedTableItem(cell)->item, selected);
- }
-}
-
-void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const
+void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select)
{
const int cellIndex = modelIndexToCellIndex(modelIndex);
if (!loadedItems.contains(cellIndex))
return;
const QPoint cell = cellAtModelIndex(cellIndex);
- setSelectedOnDelegateItem(loadedTableItem(cell)->item, select);
+ QQuickItem *item = loadedTableItem(cell)->item;
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false);
}
-void QQuickTableViewPrivate::setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const
+void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems()
{
- if (!delegateItem->property(kRequiredProperty).toBool()) {
- // We only assign to "selected" if it's a required property. Otherwise
- // we assume (for backwards compatibility) that the property is used
- // by the delegate for something else.
- // Note: kRequiredProperty is a work-around until QMetaProperty::isRequired() works.
- return;
+ for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) {
+ const int cellIndex = *it;
+ const QPoint cell = cellAtModelIndex(cellIndex);
+ const bool selected = selectedInSelectionModel(cell);
+ QQuickItem *item = loadedTableItem(cell)->item;
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), cellIndex, item, false);
}
-
- // Note that several delegates might be in use (in case of a DelegateChooser), and
- // the delegate can also change. So we cannot cache the propertyIndex.
- const auto metaObject = delegateItem->metaObject();
- const int propertyIndex = metaObject->indexOfProperty("selected");
- const auto metaProperty = metaObject->property(propertyIndex);
- metaProperty.write(delegateItem, QVariant::fromValue(select));
}
void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*)
@@ -3048,14 +3151,7 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
const QPoint cell = cellAtModelIndex(modelIndex);
const bool selected = selectedInSelectionModel(cell);
-
- if (qobject_cast<QQmlTableInstanceModel *>(model)) {
- const bool wasRequired = model->setRequiredProperty(modelIndex, QStringLiteral("selected"), selected);
- if (wasRequired) {
- // Work-around until QMetaProperty::isRequired() works
- item->setProperty(kRequiredProperty, true);
- }
- }
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, true);
if (auto attached = getAttachedObject(object))
attached->setView(q);
@@ -3071,10 +3167,9 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
{
- auto item = static_cast<QQuickItem*>(object);
const QPoint cell = cellAtModelIndex(modelIndex);
const bool selected = selectedInSelectionModel(cell);
- setSelectedOnDelegateItem(item, selected);
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, false);
if (auto attached = getAttachedObject(object))
emit attached->reused();
@@ -3886,27 +3981,29 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing)
{
Q_D(const QQuickTableView);
- if (!boundingRect().contains(position))
+ const QPointF localPos = mapToItem(d->contentItem, position);
+ if (!d->loadedTableOuterRect.contains(localPos))
return QPoint(-1, -1);
const qreal hSpace = d->cellSpacing.width();
const qreal vSpace = d->cellSpacing.height();
- qreal currentColumnEnd = d->loadedTableOuterRect.x() - contentX();
- qreal currentRowEnd = d->loadedTableOuterRect.y() - contentY();
+ qreal currentColumnEnd = d->loadedTableOuterRect.x();
+ qreal currentRowEnd = d->loadedTableOuterRect.y();
+
int foundColumn = -1;
int foundRow = -1;
for (const int column : d->loadedColumns) {
currentColumnEnd += d->getEffectiveColumnWidth(column);
- if (position.x() < currentColumnEnd) {
+ if (localPos.x() < currentColumnEnd) {
foundColumn = column;
break;
}
currentColumnEnd += hSpace;
- if (!includeSpacing && position.x() < currentColumnEnd) {
+ if (!includeSpacing && localPos.x() < currentColumnEnd) {
// Hit spacing
return QPoint(-1, -1);
- } else if (includeSpacing && position.x() < currentColumnEnd - (hSpace / 2)) {
+ } else if (includeSpacing && localPos.x() < currentColumnEnd - (hSpace / 2)) {
foundColumn = column;
break;
}
@@ -3914,16 +4011,16 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing)
for (const int row : d->loadedRows) {
currentRowEnd += d->getEffectiveRowHeight(row);
- if (position.y() < currentRowEnd) {
+ if (localPos.y() < currentRowEnd) {
foundRow = row;
break;
}
currentRowEnd += vSpace;
- if (!includeSpacing && position.y() < currentRowEnd) {
+ if (!includeSpacing && localPos.y() < currentRowEnd) {
// Hit spacing
return QPoint(-1, -1);
}
- if (includeSpacing && position.y() < currentRowEnd - (vSpace / 2)) {
+ if (includeSpacing && localPos.y() < currentRowEnd - (vSpace / 2)) {
foundRow = row;
break;
}
@@ -4000,6 +4097,41 @@ qreal QQuickTableView::implicitRowHeight(int row) const
return d->sizeHintForRow(row);
}
+QModelIndex QQuickTableView::modelIndex(const QPoint &cell) const
+{
+ Q_D(const QQuickTableView);
+ if (cell.x() < 0 || cell.x() >= columns() || cell.y() < 0 || cell.y() >= rows())
+ return {};
+
+ auto const qaim = d->model->abstractItemModel();
+ if (!qaim)
+ return {};
+
+ return qaim->index(cell.y(), cell.x());
+}
+
+QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const
+{
+ if (!index.isValid() || index.parent().isValid())
+ return {-1, -1};
+ return {index.column(), index.row()};
+}
+
+QModelIndex QQuickTableView::modelIndex(int row, int column) const
+{
+ return modelIndex({column, row});
+}
+
+int QQuickTableView::rowAtIndex(const QModelIndex &index) const
+{
+ return cellAtIndex(index).y();
+}
+
+int QQuickTableView::columnAtIndex(const QModelIndex &index) const
+{
+ return cellAtIndex(index).x();
+}
+
void QQuickTableView::forceLayout()
{
d_func()->forceLayout();
@@ -4092,9 +4224,9 @@ void QQuickTableSectionSizeProvider::setSize(int section, qreal size)
}
// return -1.0 if no valid explicit size retrieved
-qreal QQuickTableSectionSizeProvider::size(int section)
+qreal QQuickTableSectionSizeProvider::size(int section) const
{
- Q_D(QQuickTableSectionSizeProvider);
+ Q_D(const QQuickTableSectionSizeProvider);
auto it = d->hash.find(section);
if (it != d->hash.end())
return *it;
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index 40a0fae7c1..830bef6c68 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -156,6 +156,12 @@ public:
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitColumnWidth(int column) const;
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitRowHeight(int row) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(const QPoint &cell) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QPoint cellAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
+ Q_REVISION(6, 4) Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
+
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
Q_SIGNALS:
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index bbab9f6a25..5bbdd07528 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -71,6 +71,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
+static const int kEdgeIndexNotSet = -2;
+static const int kEdgeIndexAtEnd = -3;
class FxTableItem;
class QQuickTableSectionSizeProviderPrivate;
@@ -81,7 +83,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject {
public:
QQuickTableSectionSizeProvider(QObject *parent=nullptr);
void setSize(int section, qreal size);
- qreal size(int section);
+ qreal size(int section) const;
bool resetSize(int section);
void resetAll();
@@ -202,6 +204,7 @@ public:
VerifyTable,
LayoutTable,
LoadAndUnloadAfterLayout,
+ CancelOvershoot,
PreloadColumns,
PreloadRows,
MovePreloadedItemsToPool,
@@ -276,7 +279,7 @@ public:
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool layoutWarningIssued = false;
+ mutable bool layoutWarningIssued = false;
bool polishing = false;
bool syncVertically = false;
bool syncHorizontally = false;
@@ -295,9 +298,9 @@ public:
QQuickTableSectionSizeProvider rowHeights;
QQuickTableSectionSizeProvider columnWidths;
- EdgeRange cachedNextVisibleEdgeIndex[4];
- EdgeRange cachedColumnWidth;
- EdgeRange cachedRowHeight;
+ mutable EdgeRange cachedNextVisibleEdgeIndex[4];
+ mutable EdgeRange cachedColumnWidth;
+ mutable EdgeRange cachedRowHeight;
// TableView uses contentWidth/height to report the size of the table (this
// will e.g make scrollbars written for Flickable work out of the box). This
@@ -358,14 +361,16 @@ public:
QSize calculateTableSize();
void updateTableSize();
- inline bool isColumnHidden(int column);
- inline bool isRowHidden(int row);
+ inline bool isColumnHidden(int column) const;
+ inline bool isRowHidden(int row) const;
qreal getColumnLayoutWidth(int column);
qreal getRowLayoutHeight(int row);
- qreal getColumnWidth(int column);
- qreal getRowHeight(int row);
+ qreal getColumnWidth(int column) const;
+ qreal getRowHeight(int row) const;
+ qreal getEffectiveRowY(int row) const;
qreal getEffectiveRowHeight(int row) const;
+ qreal getEffectiveColumnX(int column) const;
qreal getEffectiveColumnWidth(int column) const;
int topRow() const { return *loadedRows.cbegin(); }
@@ -396,11 +401,11 @@ public:
void syncLoadedTableFromLoadRequest();
void shiftLoadedTableRect(const QPointF newPosition);
- int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
- int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
- bool allColumnsLoaded();
- bool allRowsLoaded();
- inline int edgeToArrayIndex(Qt::Edge edge);
+ int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const;
+ int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const;
+ inline bool atTableEnd(Qt::Edge edge) const { return nextVisibleEdgeIndexAroundLoadedTable(edge) == kEdgeIndexAtEnd; }
+ inline bool atTableEnd(Qt::Edge edge, int startIndex) const { return nextVisibleEdgeIndex(edge, startIndex) == kEdgeIndexAtEnd; }
+ inline int edgeToArrayIndex(Qt::Edge edge) const;
void clearEdgeSizeCache();
bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
@@ -421,7 +426,7 @@ public:
void unloadItem(const QPoint &cell);
void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
void unloadEdge(Qt::Edge edge);
- void loadAndUnloadVisibleEdges();
+ void loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested);
void drainReusePoolAfterLoadRequest();
void processLoadRequest();
@@ -433,6 +438,7 @@ public:
void layoutAfterLoadingInitialTable();
void adjustViewportXAccordingToAlignment();
void adjustViewportYAccordingToAlignment();
+ void cancelOvershootAfterLayout();
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
@@ -473,10 +479,9 @@ public:
void syncViewportPosRecursive();
bool selectedInSelectionModel(const QPoint &cell) const;
- void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const;
- void updateSelectedOnAllDelegateItems() const;
- void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const;
- void setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const;
+ void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected);
+ void updateSelectedOnAllDelegateItems();
+ void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select);
void fetchMoreData();
@@ -486,6 +491,11 @@ public:
inline QString tableLayoutToString() const;
void dumpTable() const;
+ void setRequiredProperty(const char *property,
+ const QVariant &value,
+ int serializedModelIndex,
+ QObject *object, bool init);
+
// QQuickSelectable
QQuickItem *selectionPointerHandlerTarget() const override;
void setSelectionStartPos(const QPointF &pos) override;
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index f5dca6e0f5..66baa7ed7c 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -772,11 +772,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
const bool pixelSize = font.pixelSize() != -1;
QString layoutText = layout.text();
- int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
- int smallFont = fontSizeMode() != QQuickText::FixedSize
- ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
- : largeFont;
- int scaledFontSize = largeFont;
+ const qreal minimumSize = pixelSize
+ ? static_cast<qreal>(minimumPixelSize())
+ : minimumPointSize();
+ qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
+ qreal smallFont = fontSizeMode() != QQuickText::FixedSize
+ ? qMin<qreal>(minimumSize, largeFont)
+ : largeFont;
+ qreal scaledFontSize = largeFont;
+ const qreal sizeFittingThreshold(0.01);
bool widthChanged = false;
widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit);
@@ -805,7 +809,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (pixelSize)
scaledFont.setPixelSize(scaledFontSize);
else
- scaledFont.setPointSize(scaledFontSize);
+ scaledFont.setPointSizeF(scaledFontSize);
if (layout.font() != scaledFont)
layout.setFont(scaledFont);
}
@@ -1069,40 +1073,45 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (!horizontalFit && !verticalFit)
break;
+ // Can't find a better fit
+ if (qFuzzyCompare(smallFont, largeFont))
+ break;
+
// Try and find a font size that better fits the dimensions of the element.
if (horizontalFit) {
if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
widthExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
- if (pixelSize)
- scaledFont.setPixelSize(scaledFontSize);
- else
- scaledFont.setPointSize(scaledFontSize);
+
continue;
} else if (!verticalFit) {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
if (verticalFit) {
if (truncateHeight || unelidedRect.height() > maxHeight) {
heightExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
} else {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
}
diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp
index 271df508de..e6cf4a8305 100644
--- a/src/quick/items/qquicktreeview.cpp
+++ b/src/quick/items/qquicktreeview.cpp
@@ -149,126 +149,129 @@
\note this function will not affect the model, only
the visual representation in the view.
- \sa collapse(), isExpanded()
+ \sa collapse(), isExpanded(), expandRecursively()
*/
/*!
- \qmlmethod QtQuick::TreeView::collapse(row)
+ \qmlmethod QtQuick::TreeView::expandRecursively(row = -1, depth = -1)
+ \since 6.4
- Collapses the tree node at the given \a row in the view.
+ Expands the tree node at the given \a row in the view recursively down to
+ \a depth. \a depth should be relative to the depth of \a row. If
+ \a depth is \c -1, the tree will be expanded all the way down to all leaves.
+
+ For a model that has more than one root, you can also call this function
+ with \a row equal to \c -1. This will expand all roots. Hence, calling
+ expandRecursively(-1, -1), or simply expandRecursively(), will expand
+ all nodes in the model.
\a row should be the row in the view (table row), and not a row in the model.
- \note this function will not affect the model, only
- the visual representation in the view.
+ \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more} data.
+ \note This function will not affect the model, only the visual representation in the view.
+ \warning If the model contains a large number of items, this function will
+ take some time to execute.
- \sa expand(), isExpanded()
+ \sa collapseRecursively(), expand(), collapse(), isExpanded(), depth()
*/
/*!
- \qmlmethod QtQuick::TreeView::toggleExpanded(row)
+ \qmlmethod QtQuick::TreeView::expandToIndex(QModelIndex index)
+ \since 6.4
- Toggles if the tree node at the given \a row should be expanded.
- This is a convenience for doing:
+ Expands the tree from the given model \a index, and recursively all the way up
+ to the root. The result will be that the delegate item that represents \a index
+ becomes visible in the view (unless it ends up outside the viewport). To
+ ensure that the row ends up visible in the viewport, you can do:
\code
- if (isExpanded(row))
- collapse(row)
- else
- expand(row)
+ expandToIndex(index)
+ forceLayout()
+ positionViewAtRow(rowAtIndex(index), Qt.AlignVCenter)
\endcode
- \a row should be the row in the view (table row), and not a row in the model.
+ \sa expand(), expandRecursively()
*/
/*!
- \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(row, column)
-
- Returns the \l QModelIndex that maps to \a row and \a column in the view.
-
- \a row and \a column should be the row and column in the view (table row and
- table column), and not a row and column in the model.
-
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- index in the tree model maps to the given row and column in the view.
+ \qmlmethod QtQuick::TreeView::collapse(row)
- \sa rowAtIndex(), columnAtIndex()
-*/
+ Collapses the tree node at the given \a row in the view.
-/*!
- \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(point cell)
+ \a row should be the row in the view (table row), and not a row in the model.
- Convenience function for doing:
- \code
- modelIndex(cell.y, cell.x)
- \endcode
+ \note this function will not affect the model, only
+ the visual representation in the view.
- A cell is simply a \l point that combines row and column into
- a single type. Note that \c point.x will map to the column, and
- \c point.y will map to the row.
+ \sa expand(), isExpanded()
*/
/*!
- \qmlmethod int QtQuick::TreeView::rowAtIndex(modelIndex)
-
- Returns the row in the view that maps to \a modelIndex in the model.
+ \qmlmethod QtQuick::TreeView::collapseRecursively(row = -1)
+ \since 6.4
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- row in the view the given model index maps to.
+ Collapses the tree node at the given \a row in the view recursively down to
+ all leaves.
- \note \a modelIndex must be a \l QModelIndex.
+ For a model has more than one root, you can also call this function
+ with \a row equal to \c -1. This will collapse all roots. Hence, calling
+ collapseRecursively(-1), or simply collapseRecursively(), will collapse
+ all nodes in the model.
- \sa columnAtIndex(), modelIndex()
-*/
-
-/*!
- \qmlmethod int QtQuick::TreeView::columnAtIndex(modelIndex)
-
- Returns the column in the view that maps to \a modelIndex in the model.
-
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- column in the view the given model index maps to.
+ \a row should be the row in the view (table row), and not a row in the model.
- \note \a modelIndex must be a \l QModelIndex.
+ \note this function will not affect the model, only
+ the visual representation in the view.
- \sa rowAtIndex(), modelIndex()
+ \sa expandRecursively(), expand(), collapse(), isExpanded(), depth()
*/
/*!
- \qmlmethod point QtQuick::TreeView::cellAtIndex(modelIndex)
+ \qmlmethod QtQuick::TreeView::toggleExpanded(row)
- Convenience function for doing:
+ Toggles if the tree node at the given \a row should be expanded.
+ This is a convenience for doing:
- \c {Qt.point(columnAtIndex(}\a {modelIndex}\c{), rowAtIndex(}\a {modelIndex}\c{))}
+ \code
+ if (isExpanded(row))
+ collapse(row)
+ else
+ expand(row)
+ \endcode
- A cell is simply a \l point that combines row and column into
- a single type. Note that \c point.x will map to the column, and
- \c point.y will map to the row.
+ \a row should be the row in the view (table row), and not a row in the model.
*/
/*!
- \qmlsignal QtQuick::TreeView::expanded(row)
+ \qmlsignal QtQuick::TreeView::expanded(row, depth)
This signal is emitted when a \a row is expanded in the view.
+ \a row and \a depth will be equal to the arguments given to the call
+ that caused the expansion to happen (\l expand() or \l expandRecursively()).
+ In case of \l expand(), \a depth will always be \c 1.
+ In case of \l expandToIndex(), \a depth will be the depth of the
+ target index.
+
+ \note when a row is expanded recursively, the expanded signal will
+ only be emitted for that one row, and not for its descendants.
\sa collapsed(), expand(), collapse(), toggleExpanded()
*/
/*!
- \qmlsignal QtQuick::TreeView::collapsed(row)
+ \qmlsignal QtQuick::TreeView::collapsed(row, recursively)
This signal is emitted when a \a row is collapsed in the view.
+ \a row will be equal to the argument given to the call that caused
+ the collapse to happen (\l collapse() or \l collapseRecursively()).
+ If the row was collapsed recursively, \a recursively will be \c true.
+
+ \note when a row is collapsed recursively, the collapsed signal will
+ only be emitted for that one row, and not for its descendants.
\sa expanded(), expand(), collapse(), toggleExpanded()
*/
-static const char* kRequiredProperties = "_qt_treeview_requiredpropertymask";
// Hard-code the tree column to be 0 for now
static const int kTreeColumn = 0;
@@ -343,32 +346,6 @@ void QQuickTreeViewPrivate::dataChangedCallback(
}
}
-void QQuickTreeViewPrivate::setRequiredProperty(const char *property,
- const QVariant &value, int serializedModelIndex, QObject *object, bool init)
-{
- // Attaching a property list to the delegate item is just a
- // work-around until QMetaProperty::isRequired() works!
- const QString propertyName = QString::fromUtf8(property);
-
- if (init) {
- const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value);
- if (wasRequired) {
- QStringList propertyList = object->property(kRequiredProperties).toStringList();
- object->setProperty(kRequiredProperties, propertyList << propertyName);
- }
- } else {
- const QStringList propertyList = object->property(kRequiredProperties).toStringList();
- if (!propertyList.contains(propertyName)) {
- // We only write to properties that are required
- return;
- }
- const auto metaObject = object->metaObject();
- const int propertyIndex = metaObject->indexOfProperty(property);
- const auto metaProperty = metaObject->property(propertyIndex);
- metaProperty.write(object, value);
- }
-}
-
void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject *object, bool init)
{
Q_Q(QQuickTreeView);
@@ -420,22 +397,97 @@ bool QQuickTreeView::isExpanded(int row) const
void QQuickTreeView::expand(int row)
{
+ if (row >= 0)
+ expandRecursively(row, 1);
+}
+
+void QQuickTreeView::expandRecursively(int row, int depth)
+{
Q_D(QQuickTreeView);
- if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
+ if (row >= d->m_treeModelToTableModel.rowCount())
+ return;
+ if (row < 0 && row != -1)
+ return;
+ if (depth == 0 || depth < -1)
+ return;
+
+ auto expandRowRecursively = [=](int startRow) {
+ d->m_treeModelToTableModel.expandRecursively(startRow, depth);
+ // Update the expanded state of the startRow. The descendant rows that gets
+ // expanded will get the correct state set from initItem/itemReused instead.
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, startRow);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+ };
+
+ if (row >= 0) {
+ // Expand only one row recursively
+ const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
+ if (isExpanded && depth == 1)
+ return;
+ expandRowRecursively(row);
+ } else {
+ // Expand all root nodes recursively
+ const auto model = d->m_treeModelToTableModel.model();
+ for (int r = 0; r < model->rowCount(); ++r) {
+ const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
+ if (rootRow != -1)
+ expandRowRecursively(rootRow);
+ }
+ }
+
+ emit expanded(row, depth);
+}
+
+void QQuickTreeView::expandToIndex(const QModelIndex &index)
+{
+ Q_D(QQuickTreeView);
+
+ if (!index.isValid()) {
+ qmlWarning(this) << "index is not valid: " << index;
return;
+ }
- if (d->m_treeModelToTableModel.isExpanded(row))
+ if (index.model() != d->m_treeModelToTableModel.model()) {
+ qmlWarning(this) << "index doesn't belong to correct model: " << index;
return;
+ }
- d->m_treeModelToTableModel.expandRow(row);
+ if (rowAtIndex(index) != -1) {
+ // index is already visible
+ return;
+ }
- for (int c = leftColumn(); c <= rightColumn(); ++c) {
- const QPoint treeNodeCell(c, row);
- if (const auto item = itemAtCell(treeNodeCell))
- d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ int depth = 1;
+ QModelIndex parent = index.parent();
+ int row = rowAtIndex(parent);
+
+ while (parent.isValid()) {
+ if (row != -1) {
+ // The node is already visible, since it maps to a row in the table!
+ d->m_treeModelToTableModel.expandRow(row);
+
+ // Update the state of the already existing delegate item
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, row);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+
+ // When we hit a node that is visible, we know that all other nodes
+ // up to the parent have to be visible as well, so we can stop.
+ break;
+ } else {
+ d->m_treeModelToTableModel.expand(parent);
+ parent = parent.parent();
+ row = rowAtIndex(parent);
+ depth++;
+ }
}
- emit expanded(row);
+ emit expanded(row, depth);
}
void QQuickTreeView::collapse(int row)
@@ -455,7 +507,42 @@ void QQuickTreeView::collapse(int row)
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
}
- emit collapsed(row);
+ emit collapsed(row, false);
+}
+
+void QQuickTreeView::collapseRecursively(int row)
+{
+ Q_D(QQuickTreeView);
+ if (row >= d->m_treeModelToTableModel.rowCount())
+ return;
+ if (row < 0 && row != -1)
+ return;
+
+ auto collapseRowRecursive = [=](int startRow) {
+ // Always collapse descendants recursively,
+ // even if the top row itself is already collapsed.
+ d->m_treeModelToTableModel.collapseRecursively(startRow);
+ // Update the expanded state of the (still visible) startRow
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, startRow);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+ };
+
+ if (row >= 0) {
+ collapseRowRecursive(row);
+ } else {
+ // Collapse all root nodes recursively
+ const auto model = d->m_treeModelToTableModel.model();
+ for (int r = 0; r < model->rowCount(); ++r) {
+ const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
+ if (rootRow != -1)
+ collapseRowRecursive(rootRow);
+ }
+ }
+
+ emit collapsed(row, true);
}
void QQuickTreeView::toggleExpanded(int row)
@@ -466,32 +553,22 @@ void QQuickTreeView::toggleExpanded(int row)
expand(row);
}
-QModelIndex QQuickTreeView::modelIndex(int row, int column) const
+QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const
{
Q_D(const QQuickTreeView);
- const QModelIndex tableIndex = d->m_treeModelToTableModel.index(row, column);
+ const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
return d->m_treeModelToTableModel.mapToModel(tableIndex);
}
-QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const
-{
- return modelIndex(cell.y(), cell.x());
-}
-
-int QQuickTreeView::rowAtIndex(const QModelIndex &index) const
-{
- return d_func()->m_treeModelToTableModel.mapFromModel(index).row();
-}
-
-int QQuickTreeView::columnAtIndex(const QModelIndex &index) const
-{
- return d_func()->m_treeModelToTableModel.mapFromModel(index).column();
-}
-
QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const
{
const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
return QPoint(tableIndex.column(), tableIndex.row());
}
+QModelIndex QQuickTreeView::modelIndex(int row, int column) const
+{
+ return modelIndex({column, row});
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h
index c3e4b5a38f..870b29d329 100644
--- a/src/quick/items/qquicktreeview_p.h
+++ b/src/quick/items/qquicktreeview_p.h
@@ -75,15 +75,17 @@ public:
Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void toggleExpanded(int row);
- Q_INVOKABLE QModelIndex modelIndex(int row, int column) const;
- Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const;
- Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
- Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
- Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
+ Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
+ Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index);
+
+ Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override;
+ Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override;
+ Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
signals:
- void expanded(int row);
- void collapsed(int row);
+ void expanded(int row, int depth);
+ void collapsed(int row, bool recursively);
private:
Q_DISABLE_COPY(QQuickTreeView)
diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h
index db974aefc9..47d87d2ac1 100644
--- a/src/quick/items/qquicktreeview_p_p.h
+++ b/src/quick/items/qquicktreeview_p_p.h
@@ -77,7 +77,6 @@ public:
const QModelIndex &bottomRight,
const QVector<int> &roles);
- void setRequiredProperty(const char *property, const QVariant &value, int serializedModelIndex, QObject *object, bool init);
void updateRequiredProperties(int serializedModelIndex, QObject *object, bool init);
public:
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 1bc266bbbf..7018a84395 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -60,17 +60,12 @@
#include <QtCore/QWeakPointer>
#include <QtQml/qqmlengine.h>
-#include <private/qv4object_p.h>
#include "qquickwindow_p.h"
#include "qquickitemchangelistener_p.h"
QT_BEGIN_NAMESPACE
-namespace QV4 {
-struct ExecutionEngine;
-}
-
class QQmlContext;
class QQmlError;
class QQuickItem;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 156ecdaadb..f68848444e 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -428,10 +428,10 @@ static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio)
void QQuickWindow::physicalDpiChanged()
{
Q_D(QQuickWindow);
- const qreal newPixelRatio = screen()->devicePixelRatio();
- if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio))
+ const qreal newPixelRatio = effectiveDevicePixelRatio();
+ if (qFuzzyCompare(newPixelRatio, d->lastReportedItemDevicePixelRatio))
return;
- d->devicePixelRatio = newPixelRatio;
+ d->lastReportedItemDevicePixelRatio = newPixelRatio;
if (d->contentItem)
updatePixelRatioHelper(d->contentItem, newPixelRatio);
}
@@ -514,7 +514,6 @@ void QQuickWindowPrivate::ensureCustomRenderTarget()
redirect.renderTargetDirty = false;
redirect.rt.reset(rhi);
- redirect.devicePixelRatio = customRenderTarget.devicePixelRatio();
// a default constructed QQuickRenderTarget means no redirection
if (customRenderTarget.isNull())
@@ -535,11 +534,6 @@ void QQuickWindowPrivate::syncSceneGraph()
ensureCustomRenderTarget();
- // Calculate the dpr the same way renderSceneGraph() will.
- qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (redirect.rt.renderTarget && !QQuickRenderControl::renderWindowFor(q))
- devicePixelRatio = redirect.devicePixelRatio;
-
QRhiCommandBuffer *cb = nullptr;
if (rhi) {
if (redirect.commandBuffer)
@@ -547,7 +541,7 @@ void QQuickWindowPrivate::syncSceneGraph()
else
cb = swapchain->currentFrameCommandBuffer();
}
- context->prepareSync(devicePixelRatio, cb, graphicsConfig);
+ context->prepareSync(q->effectiveDevicePixelRatio(), cb, graphicsConfig);
animationController->beforeNodeSync();
@@ -646,36 +640,21 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
if (flipY)
matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (redirect.rt.renderTarget) {
- const QSize pixelSize = redirect.rt.renderTarget->pixelSize();
- QRect rect(QPoint(0, 0), pixelSize);
- renderer->setDeviceRect(rect);
- renderer->setViewportRect(rect);
- if (QQuickRenderControl::renderWindowFor(q)) {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags);
- renderer->setDevicePixelRatio(devicePixelRatio);
- } else {
- const QSizeF logicalSize = pixelSize / redirect.devicePixelRatio;
- renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags);
- renderer->setDevicePixelRatio(redirect.devicePixelRatio);
- }
- } else {
- QSize pixelSize;
- QSizeF logicalSize;
- if (surfaceSize.isEmpty()) {
- pixelSize = size * devicePixelRatio;
- logicalSize = size;
- } else {
- pixelSize = surfaceSize;
- logicalSize = QSizeF(surfaceSize) / devicePixelRatio;
- }
- QRect rect(QPoint(0, 0), pixelSize);
- renderer->setDeviceRect(rect);
- renderer->setViewportRect(rect);
- renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
- renderer->setDevicePixelRatio(devicePixelRatio);
- }
+ QSize pixelSize;
+ if (redirect.rt.renderTarget)
+ pixelSize = redirect.rt.renderTarget->pixelSize();
+ else if (surfaceSize.isEmpty())
+ pixelSize = size * devicePixelRatio;
+ else
+ pixelSize = surfaceSize;
+ QSizeF logicalSize = QSizeF(pixelSize) / devicePixelRatio;
+
+ renderer->setDevicePixelRatio(devicePixelRatio);
+ renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize));
+ renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize));
+ renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags);
if (rhi) {
context->renderNextRhiFrame(renderer);
@@ -705,7 +684,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
QQuickWindowPrivate::QQuickWindowPrivate()
: contentItem(nullptr)
, dirtyItemList(nullptr)
- , devicePixelRatio(0)
+ , lastReportedItemDevicePixelRatio(0)
, context(nullptr)
, renderer(nullptr)
, windowManager(nullptr)
@@ -770,7 +749,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_ASSERT(windowManager || renderControl);
if (QScreen *screen = q->screen()) {
- devicePixelRatio = screen->devicePixelRatio();
+ lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio();
// if the screen changes, then QQuickWindow::handleScreenChanged disconnects
// and connects to the new screen
physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
@@ -1326,6 +1305,8 @@ QQuickItem *QQuickWindow::contentItem() const
\brief The item which currently has active focus or \c null if there is
no item with active focus.
+
+ \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
*/
QQuickItem *QQuickWindow::activeFocusItem() const
{
@@ -3736,21 +3717,31 @@ void QQuickWindow::runJobsAfterSwap()
}
/*!
- * Returns the device pixel ratio for this window.
- *
- * This is different from QWindow::devicePixelRatio() in that it supports
- * redirected rendering via QQuickRenderControl. When using a
- * QQuickRenderControl, the QQuickWindow is often not created, meaning it is
- * never shown and there is no underlying native window created in the
- * windowing system. As a result, querying properties like the device pixel
- * ratio cannot give correct results. Use this function instead.
- *
- * \sa QWindow::devicePixelRatio()
+ Returns the device pixel ratio for this window.
+
+ This is different from QWindow::devicePixelRatio() in that it supports
+ redirected rendering via QQuickRenderControl and QQuickRenderTarget. When
+ using a QQuickRenderControl, the QQuickWindow is often not fully created,
+ meaning it is never shown and there is no underlying native window created
+ in the windowing system. As a result, querying properties like the device
+ pixel ratio cannot give correct results. This function takes into account
+ both QQuickRenderControl::renderWindowFor() and
+ QQuickRenderTarget::devicePixelRatio(). When no redirection is in effect,
+ the result is same as QWindow::devicePixelRatio().
+
+ \sa QQuickRenderControl, QQuickRenderTarget, setRenderTarget(), QWindow::devicePixelRatio()
*/
qreal QQuickWindow::effectiveDevicePixelRatio() const
{
+ Q_D(const QQuickWindow);
QWindow *w = QQuickRenderControl::renderWindowFor(const_cast<QQuickWindow *>(this));
- return w ? w->devicePixelRatio() : devicePixelRatio();
+ if (w)
+ return w->devicePixelRatio();
+
+ if (!d->customRenderTarget.isNull())
+ return d->customRenderTarget.devicePixelRatio();
+
+ return devicePixelRatio();
}
/*!
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 0e69d63239..0fd09a1e33 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -193,7 +193,7 @@ public:
QVector<QQuickItem *> itemsToPolish;
- qreal devicePixelRatio;
+ qreal lastReportedItemDevicePixelRatio;
QMetaObject::Connection physicalDpiChangedConnection;
void updateDirtyNodes();
@@ -228,7 +228,6 @@ public:
struct Redirect {
QRhiCommandBuffer *commandBuffer = nullptr;
QQuickWindowRenderTarget rt;
- qreal devicePixelRatio = 1.0;
bool renderTargetDirty = false;
} redirect;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 95e6d35c1e..bda0085818 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -930,11 +930,10 @@ static void qsg_wipeBuffer(Buffer *buffer)
free(buffer->data);
}
-static void qsg_wipeBatch(Batch *batch, bool separateIndexBuffer)
+static void qsg_wipeBatch(Batch *batch)
{
qsg_wipeBuffer(&batch->vbo);
- if (separateIndexBuffer)
- qsg_wipeBuffer(&batch->ibo);
+ qsg_wipeBuffer(&batch->ibo);
delete batch->ubuf;
batch->stencilClipState.reset();
delete batch;
@@ -944,13 +943,12 @@ Renderer::~Renderer()
{
if (m_rhi) {
// Clean up batches and buffers
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
- qsg_wipeBatch(m_opaqueBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_opaqueBatches.at(i));
for (int i = 0; i < m_alphaBatches.size(); ++i)
- qsg_wipeBatch(m_alphaBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_alphaBatches.at(i));
for (int i = 0; i < m_batchPool.size(); ++i)
- qsg_wipeBatch(m_batchPool.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_batchPool.at(i));
}
for (Node *n : qAsConst(m_nodes))
@@ -1008,8 +1006,7 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
if (m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
- QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
- ? m_indexUploadPool : m_vertexUploadPool;
+ QDataBuffer<char> &pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
if (byteSize > pool.size())
pool.resize(byteSize);
buffer->data = pool.data();
@@ -2075,11 +2072,7 @@ void Renderer::uploadBatch(Batch *b)
ibufferSize = unmergedIndexSize;
}
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
+ map(&b->ibo, ibufferSize, true);
map(&b->vbo, bufferSize);
if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
@@ -2089,9 +2082,7 @@ void Renderer::uploadBatch(Batch *b)
if (b->merged) {
char *vertexData = b->vbo.data;
char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(useDepthBuffer()) * b->vertexCount * sizeof(float));
+ char *indexData = b->ibo.data;
quint16 iOffset16 = 0;
quint32 iOffset32 = 0;
@@ -2103,8 +2094,8 @@ void Renderer::uploadBatch(Batch *b)
const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
int indicesInSet = 0;
b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ int drawSetIndices = 0;
+ const char *indexBase = b->ibo.data;
b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
while (e) {
verticesInSet += e->node->geometry()->vertexCount();
@@ -2138,8 +2129,7 @@ void Renderer::uploadBatch(Batch *b)
}
} else {
char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
+ char *iboData = b->ibo.data;
Element *e = b->first;
while (e) {
QSGGeometry *g = e->node->geometry();
@@ -2201,9 +2191,7 @@ void Renderer::uploadBatch(Batch *b)
if (!b->drawSets.isEmpty()) {
if (m_uint32IndexForRhi) {
- const quint32 *id = (const quint32 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint32 *id = (const quint32 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2214,9 +2202,7 @@ void Renderer::uploadBatch(Batch *b)
}
}
} else {
- const quint16 *id = (const quint16 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint16 *id = (const quint16 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2237,8 +2223,7 @@ void Renderer::uploadBatch(Batch *b)
#endif // QT_NO_DEBUG_OUTPUT
unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->ibo, true);
if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
@@ -2669,6 +2654,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms,
ps->setFlags(flags);
ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
+ ps->setPolygonMode(m_gstate.polygonMode);
QRhiGraphicsPipeline::TargetBlend blend;
blend.colorWrite = m_gstate.colorWrite;
@@ -2808,13 +2794,14 @@ static void rendererToMaterialGraphicsState(QSGMaterialShader::GraphicsPipelineS
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
-
+ Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::Line) == int(QRhiGraphicsPipeline::Line));
dst->srcColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->srcColor);
dst->dstColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->dstColor);
dst->colorWrite = QSGMaterialShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
dst->cullMode = QSGMaterialShader::GraphicsPipelineState::CullMode(src->cullMode);
+ dst->polygonMode = QSGMaterialShader::GraphicsPipelineState::PolygonMode(src->polygonMode);
}
static void materialToRendererGraphicsState(GraphicsState *dst,
@@ -2825,6 +2812,7 @@ static void materialToRendererGraphicsState(GraphicsState *dst,
dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+ dst->polygonMode = QRhiGraphicsPipeline::PolygonMode(src->polygonMode);
}
void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
@@ -3695,7 +3683,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
if (largestVBO * 2 < m_vertexUploadPool.size())
m_vertexUploadPool.resize(largestVBO * 2);
- if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size())
+ if (largestIBO * 2 < m_indexUploadPool.size())
m_indexUploadPool.resize(largestIBO * 2);
if (Q_UNLIKELY(debug_render())) {
@@ -3727,6 +3715,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
m_gstate.blending = false;
m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.polygonMode = QRhiGraphicsPipeline::Fill;
m_gstate.colorWrite = QRhiGraphicsPipeline::R
| QRhiGraphicsPipeline::G
| QRhiGraphicsPipeline::B
@@ -4042,7 +4031,8 @@ bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
&& a.stencilTest == b.stencilTest
&& a.sampleCount == b.sampleCount
&& a.drawMode == b.drawMode
- && a.lineWidth == b.lineWidth;
+ && a.lineWidth == b.lineWidth
+ && a.polygonMode == b.polygonMode;
}
bool operator!=(const GraphicsState &a, const GraphicsState &b) noexcept
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 241f5748b6..5cfc6c54c6 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -640,6 +640,7 @@ struct GraphicsState
int sampleCount = 1;
QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
float lineWidth = 1.0f;
+ QRhiGraphicsPipeline::PolygonMode polygonMode = QRhiGraphicsPipeline::Fill;
};
bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
index bd17fd365a..3eb544f94d 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -684,6 +684,23 @@ bool QSGMaterialShader::updateGraphicsPipelineState(RenderState &state, Graphics
*/
/*!
+ \enum QSGMaterialShader::GraphicsPipelineState::PolygonMode
+ \since 6.4
+ \brief Specifies the polygon rasterization mode
+
+ Polygon Mode (Triangle Fill Mode in Metal, Fill Mode in D3D) specifies
+ the fill mode used when rasterizing polygons. Polygons may be drawn as
+ solids (Fill), or as a wire mesh (Line).
+
+ \warning OpenGL ES does not support the \c{Line} polygon mode. OpenGL ES
+ will rasterize all polygons as filled no matter what polygon mode is set.
+ Using \c{Line} will make your application non-portable.
+
+ \value Fill The interior of the polygon is filled (default)
+ \value Line Boundary edges of the polygon are drawn as line segments.
+ */
+
+/*!
Returns the accumulated opacity to be used for rendering.
*/
float QSGMaterialShader::RenderState::opacity() const
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
index 566f954b06..a3c8afb875 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -130,12 +130,18 @@ public:
CullBack
};
+ enum PolygonMode {
+ Fill,
+ Line,
+ };
+
bool blendEnable;
BlendFactor srcColor;
BlendFactor dstColor;
ColorMask colorWrite;
QColor blendConstant;
CullMode cullMode;
+ PolygonMode polygonMode;
// This struct is extensible while keeping BC since apps only ever get
// a ptr to the struct, it is not created by them.
};
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 19b8cf9354..49dc056c89 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -40,6 +40,7 @@
#ifndef QSGNODE_H
#define QSGNODE_H
+#include <QtCore/qlist.h>
#include <QtQuick/qsggeometry.h>
#include <QtGui/QMatrix4x4>
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index aa9f78f2c5..7d71c5248c 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -53,6 +53,7 @@
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgtexture.h>
+#include <QtQuick/qquickpainteditem.h>
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
#include <QtGui/qbrush.h>
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 8579cb5e2a..a4337a58ca 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -62,7 +62,6 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
, m_rhiAtlasManager(nullptr)
, m_currentFrameCommandBuffer(nullptr)
, m_currentFrameRenderPass(nullptr)
- , m_separateIndexBuffer(false)
, m_useDepthBufferFor2D(true)
, m_glyphCacheResourceUpdates(nullptr)
{
@@ -87,8 +86,6 @@ void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *par
m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
if (!m_rhiAtlasManager)
m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
- // unlike OpenGL (and like WebGL), QRhi does not guarantee buffer usage types can be mixed
- m_separateIndexBuffer = true;
m_glyphCacheResourceUpdates = nullptr;
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index e96bf045b5..a97347eaf0 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -120,7 +120,6 @@ public:
virtual void initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant);
int maxTextureSize() const override { return m_maxTextureSize; }
- bool separateIndexBuffer() const { return m_separateIndexBuffer; }
bool useDepthBufferFor2D() const { return m_useDepthBufferFor2D; }
int msaaSampleCount() const { return m_initParams.sampleCount; }
@@ -159,7 +158,6 @@ protected:
QRhiCommandBuffer *m_currentFrameCommandBuffer;
QRhiRenderPassDescriptor *m_currentFrameRenderPass;
qreal m_currentDevicePixelRatio;
- bool m_separateIndexBuffer;
bool m_useDepthBufferFor2D;
QRhiResourceUpdateBatch *m_glyphCacheResourceUpdates;
};
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 98823794c4..b96f75ddbd 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -105,10 +105,6 @@ void QSGRenderLoop::cleanup()
}
delete s_instance;
s_instance = nullptr;
-
-#ifdef ENABLE_DEFAULT_BACKEND
- QSGRhiSupport::cleanupDefaultVulkanInstance();
-#endif
}
QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 7a2aeab7a6..da83e82b4b 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -39,14 +39,15 @@
#include "qsgrhisupport_p.h"
#include "qsgcontext_p.h"
-# include "qsgdefaultrendercontext_p.h"
+#include "qsgdefaultrendercontext_p.h"
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtGui/qwindow.h>
+
#if QT_CONFIG(vulkan)
-#include <QtGui/qvulkaninstance.h>
+#include <QtGui/private/qvulkandefaultinstance_p.h>
#endif
#include <QOperatingSystemVersion>
@@ -54,69 +55,6 @@
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(vulkan)
-QVulkanInstance *s_vulkanInstance = nullptr;
-#endif
-
-QVulkanInstance *QSGRhiSupport::defaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- QSGRhiSupport *inst = QSGRhiSupport::instance();
- if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan)
- return nullptr;
-
- if (!s_vulkanInstance) {
- s_vulkanInstance = new QVulkanInstance;
-
- // With a Vulkan implementation >= 1.1 we can check what
- // vkEnumerateInstanceVersion() says and request 1.2 or 1.1 based on the
- // result. To prevent future surprises, be conservative and ignore any > 1.2
- // versions for now. For 1.0 implementations nothing will be requested, the
- // default 0 in VkApplicationInfo means 1.0.
- //
- // Vulkan 1.0 is actually sufficient for 99% of Qt Quick (3D)'s
- // functionality. In addition, Vulkan implementations tend to enable 1.1 and 1.2
- // functionality regardless of the VkInstance API request. However, the
- // validation layer seems to take this fairly seriously, so we should be
- // prepared for using 1.1 and 1.2 features in a fully correct manner. This also
- // helps custom Vulkan code in applications, which is not under out control; it
- // is ideal if Vulkan 1.1 and 1.2 are usable without requiring such applications
- // to create their own QVulkanInstance just to be able to make an appropriate
- // setApiVersion() call on it.
-
- const QVersionNumber supportedVersion = s_vulkanInstance->supportedApiVersion();
- if (supportedVersion >= QVersionNumber(1, 2))
- s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
- else if (supportedVersion >= QVersionNumber(1, 1))
- s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
- qCDebug(QSG_LOG_INFO) << "Requesting Vulkan API" << s_vulkanInstance->apiVersion()
- << "Instance-level version was reported as" << supportedVersion;
-
- if (inst->isDebugLayerRequested())
- s_vulkanInstance->setLayers({ "VK_LAYER_KHRONOS_validation" });
-
- s_vulkanInstance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
-
- if (!s_vulkanInstance->create()) {
- qWarning("Failed to create Vulkan instance");
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
- }
- }
- return s_vulkanInstance;
-#else
- return nullptr;
-#endif
-}
-
-void QSGRhiSupport::cleanupDefaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
-#endif
-}
-
QSGRhiSupport::QSGRhiSupport()
: m_settingsApplied(false),
m_enableRhi(false),
@@ -256,21 +194,14 @@ void QSGRhiSupport::applySettings()
void QSGRhiSupport::adjustToPlatformQuirks()
{
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-
- // ### For now just create a throwaway QRhi instance. This will be replaced
- // by a more lightweight way, once a helper function is added gui/rhi.
-
// A macOS VM may not have Metal support at all. We have to decide at this
// point, it will be too late afterwards, and the only way is to see if
// MTLCreateSystemDefaultDevice succeeds.
if (m_rhiBackend == QRhi::Metal) {
QRhiMetalInitParams rhiParams;
- QRhi *tempRhi = QRhi::create(m_rhiBackend, &rhiParams, {});
- if (!tempRhi) {
+ if (!QRhi::probe(m_rhiBackend, &rhiParams)) {
m_rhiBackend = QRhi::OpenGLES2;
qCDebug(QSG_LOG_INFO, "Metal does not seem to be supported. Falling back to OpenGL.");
- } else {
- delete tempRhi;
}
}
#endif
@@ -574,8 +505,14 @@ void QSGRhiSupport::prepareWindowForRhi(QQuickWindow *window)
// always be under the application's control then (since the default
// instance we could create here would not be configurable by the
// application in any way, and that is often not acceptable).
- if (!window->vulkanInstance() && !wd->renderControl)
- window->setVulkanInstance(QSGRhiSupport::defaultVulkanInstance());
+ if (!window->vulkanInstance() && !wd->renderControl) {
+ QVulkanInstance *vkinst = QVulkanDefaultInstance::instance();
+ if (vkinst)
+ qCDebug(QSG_LOG_INFO) << "Got Vulkan instance from QVulkanDefaultInstance, requested api version was" << vkinst->apiVersion();
+ else
+ qCDebug(QSG_LOG_INFO) << "No Vulkan instance from QVulkanDefaultInstance, expect problems";
+ window->setVulkanInstance(vkinst);
+ }
}
#else
Q_UNUSED(window);
@@ -648,6 +585,8 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QO
#endif
#if QT_CONFIG(vulkan)
if (backend == QRhi::Vulkan) {
+ if (isDebugLayerRequested())
+ QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation, true);
QRhiVulkanInitParams rhiParams;
prepareWindowForRhi(window); // sets a vulkanInstance if not yet present
rhiParams.inst = window->vulkanInstance();
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
index 90cc8328f9..101941875e 100644
--- a/src/quick/scenegraph/qsgrhisupport_p.h
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -77,7 +77,6 @@
QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class QVulkanInstance;
class QOffscreenSurface;
// Opting in/out of QRhi and choosing the default/requested backend is managed
@@ -85,9 +84,6 @@ class QOffscreenSurface;
// creating a render loop. A well-written render loop sets up its QRhi and
// related machinery using the helper functions in here.
//
-// cleanup() must be called to perform global (not per thread) cleanup, such
-// as, destroying the QVulkanInstance (if one was created in vulkanInstance()).
-//
// In addition, the class provides handy conversion and query stuff for the
// renderloop and the QSGRendererInterface implementations.
//
@@ -96,8 +92,6 @@ class Q_QUICK_PRIVATE_EXPORT QSGRhiSupport
public:
static QSGRhiSupport *instance_internal();
static QSGRhiSupport *instance();
- static QVulkanInstance *defaultVulkanInstance();
- static void cleanupDefaultVulkanInstance();
static int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi);
static QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiCommandBuffer *cb, QRhiTexture *src = nullptr);
static void checkEnvQSgInfo();
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
index 2a7be48d3d..5541c4f2da 100644
--- a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
@@ -67,8 +67,13 @@ Manager::Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSu
, m_rhi(rc->rhi())
{
const int maxSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
- int w = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfacePixelSize.width() - 1))));
- int h = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfacePixelSize.height() - 1))));
+ // surfacePixelSize is just a hint that was passed in when initializing the
+ // rendercontext, likely based on the window size, if it was available,
+ // that is. Therefore, it may be anything, incl. zero and negative.
+ const int widthHint = qMax(1, surfacePixelSize.width());
+ const int heightHint = qMax(1, surfacePixelSize.height());
+ int w = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(widthHint - 1))));
+ int h = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(heightHint - 1))));
if (maybeSurface && maybeSurface->surfaceClass() == QSurface::Window) {
QWindow *window = static_cast<QWindow *>(maybeSurface);
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 82cdd03acc..92f66af011 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -309,6 +309,8 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o);
+ Q_ASSERT(m_texture);
+ Q_ASSERT(other->texture());
const qint64 diff = m_texture->comparisonKey() - other->texture()->comparisonKey();
if (diff != 0)
return diff < 0 ? -1 : 1;
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index 0ed3990738..e850249b65 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -210,6 +210,32 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \qmlproperty StyleHints Application::styleHints
+
+ The \c styleHints property provides platform-specific style hints and settings.
+ See the \l QStyleHints documentation for further details.
+
+ The following example uses \c styleHints to determine whether an
+ item should gain focus on mouse press or touch release:
+ \code
+ import QtQuick
+
+ MouseArea {
+ id: button
+
+ onPressed: {
+ if (!Application.styleHints.setFocusOnTouchRelease)
+ button.forceActiveFocus()
+ }
+ onReleased: {
+ if (Application.styleHints.setFocusOnTouchRelease)
+ button.forceActiveFocus()
+ }
+ }
+ \endcode
+ */
+
+/*!
\qmlsignal Application::aboutToQuit()
This signal is emitted when the application is about to quit the main
@@ -273,6 +299,11 @@ QString QQuickApplication::displayName() const
return QGuiApplication::applicationDisplayName();
}
+QStyleHints *QQuickApplication::styleHints()
+{
+ return QGuiApplication::styleHints();
+}
+
void QQuickApplication::setDisplayName(const QString &displayName)
{
return QGuiApplication::setApplicationDisplayName(displayName);
diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h
index dd67b4b11d..d8f4b3a6ee 100644
--- a/src/quick/util/qquickapplication_p.h
+++ b/src/quick/util/qquickapplication_p.h
@@ -58,6 +58,7 @@
#include <QtQml/private/qqmlglobal_p.h>
#include <QtGui/qfont.h>
+#include <QtGui/qstylehints.h>
#include <QtCore/qobject.h>
@@ -73,6 +74,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickApplication : public QQmlApplication
Q_PROPERTY(QFont font READ font CONSTANT)
Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QQmlListProperty<QQuickScreenInfo> screens READ screens NOTIFY screensChanged)
+ Q_PROPERTY(QStyleHints *styleHints READ styleHints CONSTANT)
QML_NAMED_ELEMENT(Application)
QML_SINGLETON
@@ -89,6 +91,7 @@ public:
QQmlListProperty<QQuickScreenInfo> screens();
QString displayName() const;
void setDisplayName(const QString &displayName);
+ QStyleHints *styleHints();
Q_SIGNALS:
void activeChanged();
diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp
index 3242defceb..a5c40f6139 100644
--- a/src/quick/util/qquickfontloader.cpp
+++ b/src/quick/util/qquickfontloader.cpp
@@ -58,6 +58,7 @@
#endif
#include <QtCore/QCoreApplication>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <QtGui/private/qfontdatabase_p.h>
@@ -172,13 +173,10 @@ public:
void reset()
{
- QVector<QQuickFontObject *> deleted;
- QHash<QUrl, QQuickFontObject*>::iterator it;
- for (it = map.begin(); it != map.end(); ++it) {
- if (!deleted.contains(it.value())) {
- deleted.append(it.value());
- delete it.value();
- }
+ QDuplicateTracker<QQuickFontObject *, 256> deleted(map.size());
+ for (QQuickFontObject *fo : std::as_const(map)) {
+ if (!deleted.hasSeen(fo))
+ delete fo;
}
map.clear();
}
diff --git a/src/quick/util/qquickforeignutils_p.h b/src/quick/util/qquickforeignutils_p.h
index 4d6288bc36..0f7869531f 100644
--- a/src/quick/util/qquickforeignutils_p.h
+++ b/src/quick/util/qquickforeignutils_p.h
@@ -67,6 +67,14 @@
QT_BEGIN_NAMESPACE
+struct QStyleHintsForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QStyleHints)
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 4)
+};
+
#if QT_CONFIG(validator)
struct QValidatorForeign
{
@@ -95,6 +103,7 @@ struct QInputMethodForeign
QML_FOREIGN(QInputMethod)
QML_NAMED_ELEMENT(InputMethod)
QML_ADDED_IN_VERSION(2, 0)
+ QML_REMOVED_IN_VERSION(6, 4)
QML_UNCREATABLE("InputMethod is an abstract class.")
};
#endif // QT_CONFIG(im)
diff --git a/src/quick/util/qquickinputmethod.cpp b/src/quick/util/qquickinputmethod.cpp
new file mode 100644
index 0000000000..09f1e63016
--- /dev/null
+++ b/src/quick/util/qquickinputmethod.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 "qquickinputmethod_p.h"
+
+#include <QtGui/qguiapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype InputMethod
+ \inqmlmodule QtQuick.
+
+ \brief Provides access to \l QInputMethod for QML applications.
+
+ The InputMethod singleton allows access to application's \l QInputMethod object
+ and all its properties and slots. See the \l QInputMethod documentation for
+ further details.
+*/
+
+QQuickInputMethod::QQuickInputMethod(QObject *parent) : QObject(parent)
+{
+ QInputMethod *inputMethod = QGuiApplication::inputMethod();
+ connect(inputMethod, &QInputMethod::anchorRectangleChanged, this,
+ &QQuickInputMethod::anchorRectangleChanged);
+ connect(inputMethod, &QInputMethod::animatingChanged, this,
+ &QQuickInputMethod::animatingChanged);
+ connect(inputMethod, &QInputMethod::cursorRectangleChanged, this,
+ &QQuickInputMethod::cursorRectangleChanged);
+ connect(inputMethod, &QInputMethod::inputDirectionChanged, this,
+ &QQuickInputMethod::inputDirectionChanged);
+ connect(inputMethod, &QInputMethod::inputItemClipRectangleChanged, this,
+ &QQuickInputMethod::inputItemClipRectangleChanged);
+ connect(inputMethod, &QInputMethod::keyboardRectangleChanged, this,
+ &QQuickInputMethod::keyboardRectangleChanged);
+ connect(inputMethod, &QInputMethod::localeChanged, this, &QQuickInputMethod::localeChanged);
+ connect(inputMethod, &QInputMethod::visibleChanged, this, &QQuickInputMethod::visibleChanged);
+}
+
+void QQuickInputMethod::commit()
+{
+ QGuiApplication::inputMethod()->commit();
+}
+void QQuickInputMethod::hide()
+{
+ QGuiApplication::inputMethod()->hide();
+}
+void QQuickInputMethod::invokeAction(QInputMethod::Action a, int cursorPosition)
+{
+ QGuiApplication::inputMethod()->invokeAction(a, cursorPosition);
+}
+void QQuickInputMethod::reset()
+{
+ QGuiApplication::inputMethod()->reset();
+}
+void QQuickInputMethod::show()
+{
+ QGuiApplication::inputMethod()->show();
+}
+void QQuickInputMethod::update(Qt::InputMethodQueries queries)
+{
+ QGuiApplication::inputMethod()->update(queries);
+}
+
+QRectF QQuickInputMethod::anchorRectangle() const
+{
+ return QGuiApplication::inputMethod()->cursorRectangle();
+}
+QRectF QQuickInputMethod::cursorRectangle() const
+{
+ return QGuiApplication::inputMethod()->cursorRectangle();
+}
+Qt::LayoutDirection QQuickInputMethod::inputDirection() const
+{
+ return QGuiApplication::inputMethod()->inputDirection();
+}
+QRectF QQuickInputMethod::inputItemClipRectangle() const
+{
+ return QGuiApplication::inputMethod()->inputItemClipRectangle();
+}
+
+QRectF QQuickInputMethod::inputItemRectangle() const
+{
+ return QGuiApplication::inputMethod()->inputItemRectangle();
+}
+void QQuickInputMethod::setInputItemRectangle(const QRectF &rect)
+{
+ QGuiApplication::inputMethod()->setInputItemRectangle(rect);
+}
+
+QTransform QQuickInputMethod::inputItemTransform() const
+{
+ return QGuiApplication::inputMethod()->inputItemTransform();
+}
+void QQuickInputMethod::setInputItemTransform(const QTransform &transform)
+{
+ QGuiApplication::inputMethod()->setInputItemTransform(transform);
+}
+
+bool QQuickInputMethod::isAnimating() const
+{
+ return QGuiApplication::inputMethod()->isAnimating();
+}
+
+bool QQuickInputMethod::isVisible() const
+{
+ return QGuiApplication::inputMethod()->isVisible();
+}
+void QQuickInputMethod::setVisible(bool visible)
+{
+ QGuiApplication::inputMethod()->setVisible(visible);
+}
+
+QRectF QQuickInputMethod::keyboardRectangle() const
+{
+ return QGuiApplication::inputMethod()->keyboardRectangle();
+}
+QLocale QQuickInputMethod::locale() const
+{
+ return QGuiApplication::inputMethod()->locale();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/util/qquickinputmethod_p.h b/src/quick/util/qquickinputmethod_p.h
new file mode 100644
index 0000000000..fe94da667b
--- /dev/null
+++ b/src/quick/util/qquickinputmethod_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 QQUICKINPUTMETHOD_P_H
+#define QQUICKINPUTMETHOD_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 <QtCore/qobject.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qinputmethod.h>
+#include <QtQml/qqml.h>
+
+#include <private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+class Q_QUICK_PRIVATE_EXPORT QQuickInputMethod : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(InputMethod)
+ QML_ADDED_IN_VERSION(6, 4)
+ QML_SINGLETON
+
+ Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
+ Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged)
+ Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle NOTIFY keyboardRectangleChanged)
+ Q_PROPERTY(QRectF inputItemClipRectangle READ inputItemClipRectangle NOTIFY
+ inputItemClipRectangleChanged)
+ Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
+ Q_PROPERTY(bool animating READ isAnimating NOTIFY animatingChanged)
+ Q_PROPERTY(QLocale locale READ locale NOTIFY localeChanged)
+ Q_PROPERTY(Qt::LayoutDirection inputDirection READ inputDirection NOTIFY inputDirectionChanged)
+public:
+ explicit QQuickInputMethod(QObject *parent = nullptr);
+
+ QRectF anchorRectangle() const;
+ QRectF cursorRectangle() const;
+ Qt::LayoutDirection inputDirection() const;
+ QRectF inputItemClipRectangle() const;
+
+ QRectF inputItemRectangle() const;
+ void setInputItemRectangle(const QRectF &rect);
+
+ QTransform inputItemTransform() const;
+ void setInputItemTransform(const QTransform &transform);
+
+ bool isAnimating() const;
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
+ QRectF keyboardRectangle() const;
+ QLocale locale() const;
+signals:
+ void anchorRectangleChanged();
+ void animatingChanged();
+ void cursorRectangleChanged();
+ void inputDirectionChanged(Qt::LayoutDirection newDirection);
+ void inputItemClipRectangleChanged();
+ void keyboardRectangleChanged();
+ void localeChanged();
+ void visibleChanged();
+
+public slots:
+ void commit();
+ void hide();
+ void invokeAction(QInputMethod::Action a, int cursorPosition);
+ void reset();
+ void show();
+ void update(Qt::InputMethodQueries queries);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKINPUTMETHOD_P_H
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index 5a4c72848c..f8af708ae7 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -62,6 +62,7 @@ QT_REQUIRE_CONFIG(quick_path);
#include <private/qtquickglobal_p.h>
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtGui/QPainterPath>
#include <QtGui/QFont>
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index f403615d83..9ecdbc1846 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -328,6 +328,7 @@ public:
QQuickImageProviderOptions::AutoTransform appliedTransform;
QColorSpace targetColorSpace;
+ QIODevice *specialDevice = nullptr;
QQuickTextureFactory *textureFactory;
QIntrusiveList<QQuickPixmap, &QQuickPixmap::dataListNode> declarativePixmaps;
@@ -895,40 +896,54 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
QImage image;
QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
QString errorStr;
- QFile f(existingImageFileForPath(localFile));
QSize readSize;
- if (f.open(QIODevice::ReadOnly)) {
- QSGTextureReader texReader(&f, localFile);
- if (backendSupport()->hasOpenGL && texReader.isTexture()) {
- QQuickTextureFactory *factory = texReader.read();
- if (factory) {
- readSize = factory->textureSize();
+
+ if (runningJob->data && runningJob->data->specialDevice) {
+ int frameCount;
+ int const frame = runningJob->data ? runningJob->data->frame : 0;
+ if (!readImage(url, runningJob->data->specialDevice, &image, &errorStr, &readSize, &frameCount,
+ runningJob->requestRegion, runningJob->requestSize,
+ runningJob->providerOptions, nullptr, frame)) {
+ errorCode = QQuickPixmapReply::Loading;
+ } else if (runningJob->data) {
+ runningJob->data->frameCount = frameCount;
+ }
+ } else {
+ QFile f(existingImageFileForPath(localFile));
+ if (f.open(QIODevice::ReadOnly)) {
+ QSGTextureReader texReader(&f, localFile);
+ if (backendSupport()->hasOpenGL && texReader.isTexture()) {
+ QQuickTextureFactory *factory = texReader.read();
+ if (factory) {
+ readSize = factory->textureSize();
+ } else {
+ errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
+ if (f.fileName() != localFile)
+ errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
+ errorCode = QQuickPixmapReply::Decoding;
+ }
+ mutex.lock();
+ if (!cancelled.contains(runningJob))
+ runningJob->postReply(errorCode, errorStr, readSize, factory);
+ mutex.unlock();
+ return;
} else {
- errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
- if (f.fileName() != localFile)
- errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
- errorCode = QQuickPixmapReply::Decoding;
+ int frameCount;
+ int const frame = runningJob->data ? runningJob->data->frame : 0;
+ if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
+ runningJob->requestRegion, runningJob->requestSize,
+ runningJob->providerOptions, nullptr, frame)) {
+ errorCode = QQuickPixmapReply::Loading;
+ if (f.fileName() != localFile)
+ errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
+ } else if (runningJob->data) {
+ runningJob->data->frameCount = frameCount;
+ }
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
- runningJob->postReply(errorCode, errorStr, readSize, factory);
- mutex.unlock();
- return;
} else {
- int frameCount;
- int const frame = runningJob->data ? runningJob->data->frame : 0;
- if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount, runningJob->requestRegion, runningJob->requestSize,
- runningJob->providerOptions, nullptr, frame)) {
- errorCode = QQuickPixmapReply::Loading;
- if (f.fileName() != localFile)
- errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
- } else if (runningJob->data) {
- runningJob->data->frameCount = frameCount;
- }
+ errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
+ errorCode = QQuickPixmapReply::Loading;
}
- } else {
- errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
- errorCode = QQuickPixmapReply::Loading;
}
mutex.lock();
if (!cancelled.contains(runningJob))
@@ -1738,6 +1753,47 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
}
}
+/*! \internal
+ Attempts to load an image from the given \a url via the given \a device.
+ This is for special cases when the QImageIOHandler can benefit from reusing
+ the I/O device, or from something extra that a subclass of QIODevice
+ carries with it. So far, this code doesn't support loading anything other
+ than a QImage, for example compressed textures. It can be added if needed.
+*/
+void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
+ const QRect &requestRegion, const QSize &requestSize,
+ const QQuickImageProviderOptions &providerOptions, int frame, int frameCount)
+{
+ auto oldD = d;
+ QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
+ QQuickPixmapStore *store = pixmapStore();
+ QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
+ iter = store->m_cache.find(key);
+ if (iter == store->m_cache.end()) {
+ if (!engine)
+ return;
+
+ d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions,
+ QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
+ d->specialDevice = device;
+ d->addToCache();
+
+ QQuickPixmapReader::readerMutex.lock();
+ d->reply = QQuickPixmapReader::instance(engine)->getImage(d);
+ if (oldD) {
+ QObject::connect(d->reply, &QQuickPixmapReply::finished, [oldD, this]() {
+ oldD->declarativePixmaps.remove(this);
+ oldD->release();
+ });
+ }
+ QQuickPixmapReader::readerMutex.unlock();
+ } else {
+ d = *iter;
+ d->addref();
+ d->declarativePixmaps.insert(this);
+ }
+}
+
void QQuickPixmap::clear()
{
if (d) {
diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h
index 5600443d31..68691b54e3 100644
--- a/src/quick/util/qquickpixmapcache_p.h
+++ b/src/quick/util/qquickpixmapcache_p.h
@@ -174,6 +174,9 @@ public:
void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize,
QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1,
qreal devicePixelRatio = 1.0);
+ void loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
+ const QRect &requestRegion, const QSize &requestSize,
+ const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
void clear();
void clear(QObject *);
diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h
index d75cfc32ee..3f0545df91 100644
--- a/src/quick/util/qquickstate_p_p.h
+++ b/src/quick/util/qquickstate_p_p.h
@@ -212,13 +212,19 @@ public:
struct OperationGuard : public QQmlGuard<QQuickStateOperation>
{
- OperationGuard(QObject *obj, QList<OperationGuard> *l) : list(l) {
+ OperationGuard(QObject *obj, QList<OperationGuard> *l) : QQmlGuard<QQuickStateOperation>(
+ OperationGuard::objectDestroyedImpl, nullptr)
+ ,list(l)
+ {
setObject(static_cast<QQuickStateOperation *>(obj));
}
QList<OperationGuard> *list;
- void objectDestroyed(QQuickStateOperation *) override {
+
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) {
+ auto This = static_cast<OperationGuard *>(guard);
// we assume priv will always be destroyed after objectDestroyed calls
- list->removeOne(*this);
+ This->list->removeOne(*This);
}
};
QList<OperationGuard> operations;
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index d664b67485..781d2cf1fb 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -376,7 +376,15 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when()) {
+ bool whenValue = state->when();
+ const QQmlProperty whenProp(state, u"when"_qs);
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually reevaluate the binding
+ if (auto abstractBinding = dynamic_cast<QQmlBinding *>( potentialWhenBinding.asAbstractBinding()))
+ whenValue = abstractBinding->evaluate().toBool();
+ if (whenValue) {
qCDebug(lcStates) << "Setting auto state due to expression";
if (currentState != state->name()) {
q->setState(state->name());
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index c309460263..7e93d87555 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
interpret the number and will accept locale specific digits, group separators, and positive
and negative signs. In addition, IntValidator is always guaranteed to accept a number
formatted according to the "C" locale.
+
+ \sa DoubleValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickIntValidator::QQuickIntValidator(QObject *parent)
@@ -131,6 +133,8 @@ void QQuickIntValidator::resetLocaleName()
it is also rejected. If \l notation is DoubleValidator.ScientificNotation,
and the input is not in the valid range, it is accecpted but invalid. The
value may yet become valid by changing the exponent.
+
+ \sa IntValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent)
@@ -210,6 +214,8 @@ void QQuickDoubleValidator::resetLocaleName()
The RegularExpressionValidator type provides a validator, that counts as valid any string which
matches a specified regular expression.
+
+ \sa IntValidator, DoubleValidator, {Validating Input Text}
*/
/*!
\qmlproperty regularExpression QtQuick::RegularExpressionValidator::regularExpression
diff --git a/src/quickcontrols2/CMakeLists.txt b/src/quickcontrols2/CMakeLists.txt
index 4848fc1b79..33c9bfaee7 100644
--- a/src/quickcontrols2/CMakeLists.txt
+++ b/src/quickcontrols2/CMakeLists.txt
@@ -12,7 +12,6 @@ qt_internal_add_qml_module(QuickControls2
IMPORTS
QtQuick.Controls.impl/auto
OPTIONAL_IMPORTS
- QtQuick.Controls.Basic/auto
QtQuick.Controls.Fusion/auto
QtQuick.Controls.Material/auto
QtQuick.Controls.Imagine/auto
@@ -20,6 +19,8 @@ qt_internal_add_qml_module(QuickControls2
QtQuick.Controls.Windows/auto
QtQuick.Controls.macOS/auto
QtQuick.Controls.iOS/auto
+ DEFAULT_IMPORTS
+ QtQuick.Controls.Basic/auto
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
SOURCES
diff --git a/src/quickcontrols2/basic/DayOfWeekRow.qml b/src/quickcontrols2/basic/DayOfWeekRow.qml
index 203fa5900c..9e555699df 100644
--- a/src/quickcontrols2/basic/DayOfWeekRow.qml
+++ b/src/quickcontrols2/basic/DayOfWeekRow.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/basic/MonthGrid.qml b/src/quickcontrols2/basic/MonthGrid.qml
index 797a52b8a0..a77dcd655a 100644
--- a/src/quickcontrols2/basic/MonthGrid.qml
+++ b/src/quickcontrols2/basic/MonthGrid.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/basic/WeekNumberColumn.qml b/src/quickcontrols2/basic/WeekNumberColumn.qml
index fb0d0b4591..f8e3900685 100644
--- a/src/quickcontrols2/basic/WeekNumberColumn.qml
+++ b/src/quickcontrols2/basic/WeekNumberColumn.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/doc/src/qt6-changes.qdoc b/src/quickcontrols2/doc/src/qt6-changes.qdoc
index 3c3fcdb022..61b80b3839 100644
--- a/src/quickcontrols2/doc/src/qt6-changes.qdoc
+++ b/src/quickcontrols2/doc/src/qt6-changes.qdoc
@@ -253,36 +253,36 @@
\section1 Controls
- \section2 ApplicationWindow
+ \section2 Changes to ApplicationWindow
The deprecated overlay properties and attached API were removed. Use the
\l Overlay attached type instead.
- \section2 ComboBox
+ \section2 Changes to ComboBox
The \l {ComboBox::}{pressed} property is now read-only. To modify the
visual pressed state of a ComboBox, use the \l {ComboBox::}{down} property
instead.
- \section2 Container
+ \section2 Changes to Container
The deprecated \c removeItem(var) function was removed.
\l {Container::}{removeItem(Item)} or \l {Container::}{takeItem(int)} can
be used instead.
- \section2 Dialog
+ \section2 Changes to Dialog
\l {Dialog}'s \l {Dialog::}{accepted()} and \l {Dialog::}{rejected()}
signals are now emitted before \l {Popup::}{closed()} when calling
\l {Dialog::}{done()}, \l {Dialog::}{accept()} and \l {Dialog::}{reject()}.
- \section2 Menu
+ \section2 Changes to Menu
The deprecated \c removeItem(var) function was removed.
\l {Menu::}{removeItem(Item)} or \l {Menu::}{takeItem(int)} can be used
instead.
- \section2 ToolTip
+ \section2 Changes to ToolTip
\l {ToolTip}'s timeout now begins only after \l {Popup::}{opened()} has
been emitted. This results in tooltips with enter transitions being visible
@@ -291,13 +291,13 @@
visually check tooltips in your application and adjust timeouts if
necessary.
- \section2 StackView
+ \section2 Changes to StackView
The StackView.Transition enum value was deprecated. The operation argument
can be omitted in order to use the default transition for any given
operation.
- \section2 Tumbler
+ \section2 Changes to Tumbler
\l {Item::}{implicitWidth} and \l {Item::}{implicitHeight} must now be
provided for \l {Tumbler}'s \l {Control::}{contentItem}, making it
diff --git a/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc b/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
index 6fa4447c83..3222eabf10 100644
--- a/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
+++ b/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
@@ -321,6 +321,14 @@
it's important not to import \c {QtQuick.Controls}. Doing so will
prevent the QML from being compiled by the QML compiler.
+ \section4 Implement types used by other types
+
+ Suppose you were using ScrollViews in your application, and decided that
+ you want to customize their scroll bars. It is tempting to just implement a
+ custom ScrollBar.qml and have ScrollView pick up the customized ScrollBar
+ automatically. However, this will not work. You must implement both
+ ScrollBar.qml \e and ScrollView.qml.
+
\section3 Attached properties
It is common for a style to have certain properties or attributes that
diff --git a/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc b/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
index 7b64e776dd..889947019c 100644
--- a/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
+++ b/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
@@ -38,7 +38,7 @@
\image qtquickcontrols2-styles.png
- Qt Quick Controls comes with a selection customizable styles.
+ Qt Quick Controls comes with a selection of customizable styles.
See \l {Styling Qt Quick Controls} for more details.
@@ -55,9 +55,10 @@
\section2 C++ API
- Using the \l{Qt Quick Controls C++ Classes}{C++ API} requires linking against the module library,
- either directly or through other dependencies.
- Several build tools have dedicated support for this, including CMake and qmake.
+ Using the \l{Qt Quick Controls C++ Classes}{C++ API} requires linking
+ against the module library, either directly or through other dependencies.
+ Several build tools have dedicated support for this, including CMake and
+ qmake.
\section3 Building with CMake
Use the \c find_package() command to locate the needed module components in the Qt6 package:
@@ -144,6 +145,11 @@
\endtable
\section1 Module Evolution
+
+ Qt Quick Controls was originally written with touch interfaces as the primary focus.
+ While it is already possible to develop desktop interfaces, work is ongoing to
+ provide a more native look and feel.
+
\l{Changes to Qt Quick Controls} lists important changes in the
module API and functionality that were done for the Qt 6 series of Qt.
diff --git a/src/quickcontrols2/material/qquickmaterialstyle_p.h b/src/quickcontrols2/material/qquickmaterialstyle_p.h
index 9f353cff33..b05df26be6 100644
--- a/src/quickcontrols2/material/qquickmaterialstyle_p.h
+++ b/src/quickcontrols2/material/qquickmaterialstyle_p.h
@@ -101,7 +101,7 @@ class QQuickMaterialStyle : public QQuickAttachedObject
Q_PROPERTY(QColor toolBarColor READ toolBarColor NOTIFY toolBarColorChanged FINAL)
Q_PROPERTY(QColor toolTextColor READ toolTextColor NOTIFY toolTextColorChanged FINAL)
Q_PROPERTY(QColor spinBoxDisabledIconColor READ spinBoxDisabledIconColor NOTIFY themeChanged FINAL)
- Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION 15)
+ Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION(2, 15))
Q_PROPERTY(int touchTarget READ touchTarget CONSTANT FINAL)
Q_PROPERTY(int buttonHeight READ buttonHeight CONSTANT FINAL)
diff --git a/src/quickcontrols2impl/qquickiconlabel.cpp b/src/quickcontrols2impl/qquickiconlabel.cpp
index a37ce02f6e..309a2c47d4 100644
--- a/src/quickcontrols2impl/qquickiconlabel.cpp
+++ b/src/quickcontrols2impl/qquickiconlabel.cpp
@@ -404,7 +404,7 @@ void QQuickIconLabel::setIcon(const QQuickIcon &icon)
return;
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
d->updateOrSyncImage();
}
diff --git a/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc b/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
index 92a25c3fd4..487d327497 100644
--- a/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
+++ b/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
@@ -31,8 +31,8 @@
\brief Provides QML types for creating and interacting with system dialogs.
- The Qt Quick Dialogs module allows to create and interact with system dialogs
- from QML. The module was introduced in Qt 6.2.
+ The Qt Quick Dialogs module allows you to create and interact with system
+ dialogs from QML. The module was introduced in Qt 6.2.
\section1 Using the Module
diff --git a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
index 7e7f1fcf5c..011b1bb924 100644
--- a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
@@ -111,6 +111,7 @@ Q_LOGGING_CATEGORY(lcDialogs, "qt.quick.dialogs")
\brief The base class of native dialogs.
The Dialog type provides common QML API for native platform dialogs.
+ For the non-native dialog, see \l [QML QtQuickControls]{Dialog}.
To show a native dialog, construct an instance of one of the concrete
Dialog implementations, set the desired properties, and call \l open().
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
index b74ace9aca..da899140b7 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
@@ -581,6 +581,15 @@ QUrl QQuickFileDialog::addDefaultSuffix(const QUrl &file) const
return url;
}
+void QQuickFileDialog::accept()
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ // Take the currently selected files and make them the final set of files.
+ setSelectedFiles(fileDialog->selectedFiles());
+ }
+ QQuickAbstractDialog::accept();
+}
+
QList<QUrl> QQuickFileDialog::addDefaultSuffixes(const QList<QUrl> &files) const
{
QList<QUrl> urls;
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
index ef86e11133..1d1a0e82d1 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
@@ -146,6 +146,7 @@ protected:
void onCreate(QPlatformDialogHelper *dialog) override;
void onShow(QPlatformDialogHelper *dialog) override;
void onHide(QPlatformDialogHelper *dialog) override;
+ void accept() override;
private:
QUrl addDefaultSuffix(const QUrl &file) const;
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
index 4d0b90c3a7..54c17af2f2 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
@@ -55,7 +55,7 @@ MessageDialogImpl {
implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
control.contentHeight + control.topPadding + control.bottomPadding
+ (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + control.spacing : 0))
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
leftPadding: 20
rightPadding: 20
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
index 9d40f97431..cb58015ae3 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
@@ -149,7 +149,7 @@ FontDialogImpl {
Label {
text: qsTr("Writing System")
- Layout.leftMargin: 20
+ Layout.leftMargin: 16
Layout.bottomMargin: 16
}
ComboBox{
@@ -163,7 +163,7 @@ FontDialogImpl {
id: buttonBox
standardButtons: control.standardButtons
spacing: 12
- Layout.rightMargin: 20
+ Layout.rightMargin: 16
Layout.bottomMargin: 16
}
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
index e6d7b9606f..f8a2eec922 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
@@ -58,7 +58,7 @@ MessageDialogImpl {
implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
topPadding: background ? background.topPadding : 0
leftPadding: background ? background.leftPadding : 0
@@ -135,7 +135,7 @@ MessageDialogImpl {
objectName: "detailedTextButton"
text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
- Layout.leftMargin: 20
+ Layout.leftMargin: 16
}
DialogButtonBox {
@@ -146,9 +146,11 @@ MessageDialogImpl {
verticalPadding: 20
Layout.fillWidth: true
- Layout.leftMargin: detailedTextButton.visible ? 12 : 20
- Layout.rightMargin: 20
+ Layout.leftMargin: detailedTextButton.visible ? 12 : 16
+ Layout.rightMargin: 16
}
+
+ Layout.bottomMargin: 16
}
TextArea {
@@ -159,10 +161,12 @@ MessageDialogImpl {
wrapMode: TextEdit.WordWrap
readOnly: true
+ padding: 12
+
Layout.fillWidth: true
- Layout.leftMargin: 20
- Layout.rightMargin: 20
- Layout.bottomMargin: 20
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
background: Rectangle {
color: Qt.rgba(1,1,1,1)
@@ -170,7 +174,6 @@ MessageDialogImpl {
border.color: Qt.darker(control.palette.light)
border.width: 1
}
-
}
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
index 49c517d252..d1f7857e4b 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
@@ -54,7 +54,7 @@ MessageDialogImpl {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
leftPadding: 24
rightPadding: 24
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
index 40a06367bf..0be2a67184 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
@@ -83,14 +83,6 @@ FontDialogImpl {
border.width: 1 // FlyoutBorderThemeThickness
}
- Overlay.modal: Rectangle {
- color: control.Universal.baseLowColor
- }
-
- Overlay.modeless: Rectangle {
- color: control.Universal.baseLowColor
- }
-
header: Label {
text: control.title
elide: Label.ElideRight
@@ -144,4 +136,12 @@ FontDialogImpl {
Layout.rightMargin: 24
}
}
+
+ Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
index 7194a3a6aa..ca4d3f3cea 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
@@ -54,7 +54,7 @@ MessageDialogImpl {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
padding: 24
verticalPadding: 18
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
index 3bd1cdb88b..5a7bcc6112 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
@@ -53,7 +53,7 @@ MessageDialogImpl {
implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
control.contentHeight + control.topPadding + control.bottomPadding
+ (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + control.spacing : 0))
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
leftPadding: 20
rightPadding: 20
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
index 01fc90ee57..27f19aea5b 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
@@ -73,13 +73,7 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
break;
}
case QQuickDialogType::MessageDialog: {
- auto dialog = new QQuickPlatformMessageDialog(parent);
- if (!dialog->isValid()) {
- delete dialog;
- return nullptr;
- }
-
- dialogHelper.reset(dialog);
+ dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
break;
}
default:
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
index 64c624b5ec..a51c6bd3e6 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
@@ -41,6 +41,8 @@
#include "qquickfiledialogimpl_p_p.h"
#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
@@ -135,7 +137,15 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat
}
}
- if (newSelectedFilePath.isEmpty()) {
+ static const bool preselectFirstFile = []() {
+ const QVariant envVar = qEnvironmentVariable("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE");
+ if (envVar.isValid() && envVar.canConvert<bool>())
+ return envVar.toBool();
+ return QGuiApplicationPrivate::platformTheme()->themeHint(
+ QPlatformTheme::PreselectFirstFileInDirectory).toBool();
+ }();
+
+ if (preselectFirstFile && newSelectedFilePath.isEmpty()) {
// When entering into a directory that isn't a parent of the old one, the first
// file delegate should be selected.
// TODO: is there a cheaper way to do this? QDirIterator doesn't support sorting,
diff --git a/src/quicknativestyle/items/qquickstyleitem.h b/src/quicknativestyle/items/qquickstyleitem.h
index f65e5268bf..108f080c92 100644
--- a/src/quicknativestyle/items/qquickstyleitem.h
+++ b/src/quicknativestyle/items/qquickstyleitem.h
@@ -208,8 +208,8 @@ public:
NinePatchMargins = 0x100,
SaveImage = 0x200,
};
- Q_FLAG(DebugFlag)
Q_DECLARE_FLAGS(DebugFlags, DebugFlag)
+ Q_FLAG(DebugFlags)
#endif
explicit QQuickStyleItem(QQuickItem *parent = nullptr);
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp
index 191dd81b0b..7febf39c69 100644
--- a/src/quicktemplates2/qquickabstractbutton.cpp
+++ b/src/quicktemplates2/qquickabstractbutton.cpp
@@ -51,6 +51,7 @@
# include <QtGui/private/qshortcutmap_p.h>
#endif
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQml/qqmllist.h>
@@ -235,7 +236,8 @@ void QQuickAbstractButtonPrivate::handleUngrab()
bool QQuickAbstractButtonPrivate::acceptKeyClick(Qt::Key key) const
{
- return key == Qt::Key_Space;
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ return buttonPressKeys.contains(key);
}
bool QQuickAbstractButtonPrivate::isPressAndHoldConnected()
@@ -820,7 +822,7 @@ void QQuickAbstractButton::setIcon(const QQuickIcon &icon)
{
Q_D(QQuickAbstractButton);
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
d->updateEffectiveIcon();
}
diff --git a/src/quicktemplates2/qquickaction.cpp b/src/quicktemplates2/qquickaction.cpp
index ac2b449cd1..189bb860d9 100644
--- a/src/quicktemplates2/qquickaction.cpp
+++ b/src/quicktemplates2/qquickaction.cpp
@@ -406,7 +406,7 @@ void QQuickAction::setIcon(const QQuickIcon &icon)
return;
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
emit iconChanged(icon);
}
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 85066b49f9..0ffecd6fc6 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -49,6 +49,7 @@
#include <QtCore/qglobal.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qqmlcontext.h>
@@ -1666,6 +1667,15 @@ QVariant QQuickComboBox::currentValue() const
return d->currentValue;
}
+/*!
+ \readonly
+ \since QtQuick.Controls 2.14 (Qt 5.14)
+ \qmlmethod var QtQuick.Controls::ComboBox::valueAt(int index)
+
+ Returns the value at position \a index in the combo box.
+
+ \sa indexOfValue
+*/
QVariant QQuickComboBox::valueAt(int index) const
{
Q_D(const QQuickComboBox);
@@ -1686,7 +1696,7 @@ QVariant QQuickComboBox::valueAt(int index) const
\include qquickcombobox.qdocinc functions-after-component-completion
- \sa find(), currentValue, currentIndex, valueRole
+ \sa find(), currentValue, currentIndex, valueRole, valueAt
*/
int QQuickComboBox::indexOfValue(const QVariant &value) const
{
@@ -1976,17 +1986,23 @@ void QQuickComboBox::keyPressEvent(QKeyEvent *event)
Q_D(QQuickComboBox);
QQuickControl::keyPressEvent(event);
- switch (event->key()) {
+ const auto key = event->key();
+ if (!isEditable()) {
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ if (buttonPressKeys.contains(key)) {
+ if (!event->isAutoRepeat())
+ setPressed(true);
+ event->accept();
+ return;
+ }
+ }
+
+ switch (key) {
case Qt::Key_Escape:
case Qt::Key_Back:
if (d->isPopupVisible())
event->accept();
break;
- case Qt::Key_Space:
- if (!event->isAutoRepeat())
- setPressed(true);
- event->accept();
- break;
case Qt::Key_Enter:
case Qt::Key_Return:
if (d->isPopupVisible())
@@ -2036,13 +2052,19 @@ void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
if (event->isAutoRepeat())
return;
- switch (event->key()) {
- case Qt::Key_Space:
- if (!isEditable())
- d->togglePopup(true);
- setPressed(false);
- event->accept();
- break;
+ const auto key = event->key();
+ if (!isEditable()) {
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ if (buttonPressKeys.contains(key)) {
+ if (!isEditable())
+ d->togglePopup(true);
+ setPressed(false);
+ event->accept();
+ return;
+ }
+ }
+
+ switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
if (!isEditable() || d->isPopupVisible())
diff --git a/src/quicktemplates2/qquickicon.cpp b/src/quicktemplates2/qquickicon.cpp
index 8b2f53f450..ca36de8edb 100644
--- a/src/quicktemplates2/qquickicon.cpp
+++ b/src/quicktemplates2/qquickicon.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qquickicon_p.h"
-#include "qtaggedpointer.h"
#include <private/qqmlcontextdata_p.h>
#include <private/qqmldata_p.h>
@@ -62,25 +61,11 @@ public:
QString name;
QUrl source;
+ QUrl resolvedSource;
int width = 0;
int height = 0;
QColor color = Qt::transparent;
-
- // we want DoCache as the default, and thus as the zero value
- // so that the tagged pointer can be zero initialized
- enum CacheStatus : bool { DoCache, SkipCaching };
- static_assert (DoCache == 0);
- /* We use a QTaggedPointer here to save space:
- - Without it, we would need an additional boolean, which due to
- alignment would increase the class size by sizeof(void *)
- - The pointer part stores the "owner" of the QQuickIcon, i.e.
- an object which has an icon property. We need the owner to
- access its context to resolve relative url's in the way users
- expect.
- - The tag bits are used to track whether caching is enabled.
- */
- QTaggedPointer<QObject, CacheStatus> ownerAndCache = nullptr;
-
+ bool cache = true;
};
QQuickIcon::QQuickIcon()
@@ -107,10 +92,11 @@ bool QQuickIcon::operator==(const QQuickIcon &other) const
{
return d == other.d || (d->name == other.d->name
&& d->source == other.d->source
+ && d->resolvedSource == other.d->resolvedSource
&& d->width == other.d->width
&& d->height == other.d->height
&& d->color == other.d->color
- && d->ownerAndCache == other.d->ownerAndCache);
+ && d->cache == other.d->cache);
}
bool QQuickIcon::operator!=(const QQuickIcon &other) const
@@ -157,6 +143,7 @@ void QQuickIcon::setSource(const QUrl &source)
d.detach();
d->source = source;
+ d->resolvedSource.clear();
d->resolveMask |= QQuickIconPrivate::SourceResolved;
}
@@ -164,18 +151,27 @@ void QQuickIcon::resetSource()
{
d.detach();
d->source = QString();
+ d->resolvedSource.clear();
d->resolveMask &= ~QQuickIconPrivate::SourceResolved;
}
QUrl QQuickIcon::resolvedSource() const
{
- if (QObject *owner = d->ownerAndCache.data()) {
- QQmlData *data = QQmlData::get(owner);
- if (data && data->outerContext)
- return data->outerContext->resolvedUrl(d->source);
- }
+ return d->resolvedSource.isEmpty() ? d->source : d->resolvedSource;
+}
- return d->source;
+// must be called by the property owner (e.g. Button) prior to emitting changed signal.
+void QQuickIcon::ensureRelativeSourceResolved(const QObject *owner)
+{
+ if (d->source.isEmpty())
+ return;
+ if (!d->resolvedSource.isEmpty())
+ return; // already resolved relative to (possibly) different owner
+ const QQmlData *data = QQmlData::get(owner);
+ if (!data || !data->outerContext)
+ return;
+ d.detach();
+ d->resolvedSource = data->outerContext->resolvedUrl(d->source);
}
int QQuickIcon::width() const
@@ -246,35 +242,26 @@ void QQuickIcon::resetColor()
bool QQuickIcon::cache() const
{
- return d->ownerAndCache.tag() == QQuickIconPrivate::DoCache;
+ return d->cache;
}
void QQuickIcon::setCache(bool cache)
{
- const auto cacheState = cache ? QQuickIconPrivate::DoCache : QQuickIconPrivate::SkipCaching;
- if ((d->resolveMask & QQuickIconPrivate::CacheResolved) && d->ownerAndCache.tag() == cacheState)
+ if ((d->resolveMask & QQuickIconPrivate::CacheResolved) && d->cache == cache)
return;
d.detach();
- d->ownerAndCache.setTag(cacheState);
+ d->cache = cache;
d->resolveMask |= QQuickIconPrivate::CacheResolved;
}
void QQuickIcon::resetCache()
{
d.detach();
- d->ownerAndCache.setTag(QQuickIconPrivate::DoCache);
+ d->cache = true;
d->resolveMask &= ~QQuickIconPrivate::CacheResolved;
}
-void QQuickIcon::setOwner(QObject *owner)
-{
- if (d->ownerAndCache.data() == owner)
- return;
- d.detach();
- d->ownerAndCache = owner;
-}
-
QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
{
QQuickIcon resolved = *this;
@@ -283,8 +270,10 @@ QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
if (!(d->resolveMask & QQuickIconPrivate::NameResolved))
resolved.d->name = other.d->name;
- if (!(d->resolveMask & QQuickIconPrivate::SourceResolved))
+ if (!(d->resolveMask & QQuickIconPrivate::SourceResolved)) {
resolved.d->source = other.d->source;
+ resolved.d->resolvedSource = other.d->resolvedSource;
+ }
if (!(d->resolveMask & QQuickIconPrivate::WidthResolved))
resolved.d->width = other.d->width;
@@ -296,9 +285,7 @@ QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
resolved.d->color = other.d->color;
if (!(d->resolveMask & QQuickIconPrivate::CacheResolved))
- resolved.d->ownerAndCache.setTag(other.d->ownerAndCache.tag());
-
- // owner does not change when resolving an icon
+ resolved.d->cache = other.d->cache;
return resolved;
}
diff --git a/src/quicktemplates2/qquickicon_p.h b/src/quicktemplates2/qquickicon_p.h
index 13e52b956e..60a24a97be 100644
--- a/src/quicktemplates2/qquickicon_p.h
+++ b/src/quicktemplates2/qquickicon_p.h
@@ -94,6 +94,7 @@ public:
void setSource(const QUrl &source);
void resetSource();
QUrl resolvedSource() const;
+ void ensureRelativeSourceResolved(const QObject *owner);
int width() const;
void setWidth(int width);
@@ -111,10 +112,6 @@ public:
void setCache(bool cache);
void resetCache();
- // owner is not a property - it is set internally by classes using icon
- // so that we can resolve relative URL's correctly
- void setOwner(QObject *owner);
-
QQuickIcon resolve(const QQuickIcon &other) const;
private:
diff --git a/src/quicktemplates2/qquickmenuitem.cpp b/src/quicktemplates2/qquickmenuitem.cpp
index 5ba2d792d4..c8bd1c363c 100644
--- a/src/quicktemplates2/qquickmenuitem.cpp
+++ b/src/quicktemplates2/qquickmenuitem.cpp
@@ -152,7 +152,8 @@ void QQuickMenuItemPrivate::executeArrow(bool complete)
bool QQuickMenuItemPrivate::acceptKeyClick(Qt::Key key) const
{
- return key == Qt::Key_Space || key == Qt::Key_Return || key == Qt::Key_Enter;
+ return key == Qt::Key_Return || key == Qt::Key_Enter
+ || QQuickAbstractButtonPrivate::acceptKeyClick(key);
}
QPalette QQuickMenuItemPrivate::defaultPalette() const
diff --git a/src/quicktemplates2/qquickpresshandler.cpp b/src/quicktemplates2/qquickpresshandler.cpp
index 7e6bc384ef..451c426104 100644
--- a/src/quicktemplates2/qquickpresshandler.cpp
+++ b/src/quicktemplates2/qquickpresshandler.cpp
@@ -55,7 +55,8 @@ void QQuickPressHandler::mousePressEvent(QMouseEvent *event)
pressPos = event->position();
if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) {
timer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), control);
- delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->button(), event->buttons(), event->modifiers());
+ delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
+ event->button(), event->buttons(), event->modifiers());
} else {
timer.stop();
}
diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp
index 257722a4d3..14d4e3a86f 100644
--- a/src/quicktemplates2/qquickscrollbar.cpp
+++ b/src/quicktemplates2/qquickscrollbar.cpp
@@ -165,10 +165,12 @@ static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes |
QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
{
qreal visualPos = position;
- if (minimumSize > size)
+
+ if (minimumSize > size && size != 1.0)
visualPos = position / (1.0 - size) * (1.0 - minimumSize);
- qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos), 1.0 - visualPos);
+ qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos),
+ qMax(0.0, 1.0 - visualPos));
visualPos = qBound<qreal>(0, visualPos, qMax<qreal>(0, 1.0 - visualSize));
@@ -177,7 +179,7 @@ QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
qreal QQuickScrollBarPrivate::logicalPosition(qreal position) const
{
- if (minimumSize > size)
+ if (minimumSize > size && minimumSize != 1.0)
return position * (1.0 - size) / (1.0 - minimumSize);
return position;
}
@@ -433,11 +435,11 @@ qreal QQuickScrollBar::size() const
void QQuickScrollBar::setSize(qreal size)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->size, size))
+ if (!qt_is_finite(size) || qFuzzyCompare(d->size, size))
return;
auto oldVisualArea = d->visualArea();
- d->size = size;
+ d->size = qBound(0.0, size, 1.0);
if (isComponentComplete())
d->resizeContent();
emit sizeChanged();
@@ -465,7 +467,7 @@ qreal QQuickScrollBar::position() const
void QQuickScrollBar::setPosition(qreal position)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->position, position))
+ if (!qt_is_finite(position) || qFuzzyCompare(d->position, position))
return;
auto oldVisualArea = d->visualArea();
@@ -492,7 +494,7 @@ qreal QQuickScrollBar::stepSize() const
void QQuickScrollBar::setStepSize(qreal step)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->stepSize, step))
+ if (!qt_is_finite(step) || qFuzzyCompare(d->stepSize, step))
return;
d->stepSize = step;
@@ -736,11 +738,11 @@ qreal QQuickScrollBar::minimumSize() const
void QQuickScrollBar::setMinimumSize(qreal minimumSize)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->minimumSize, minimumSize))
+ if (!qt_is_finite(minimumSize) || qFuzzyCompare(d->minimumSize, minimumSize))
return;
auto oldVisualArea = d->visualArea();
- d->minimumSize = minimumSize;
+ d->minimumSize = qBound(0.0, minimumSize, 1.0);
if (isComponentComplete())
d->resizeContent();
emit minimumSizeChanged();
diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp
index dbf5738b77..d1990a4227 100644
--- a/src/quicktemplates2/qquickspinbox.cpp
+++ b/src/quicktemplates2/qquickspinbox.cpp
@@ -595,7 +595,8 @@ void QQuickSpinBox::setEditable(bool editable)
}
\endcode
- \sa editable, textFromValue, valueFromText, {Control::locale}{locale}
+ \sa editable, textFromValue, valueFromText, {Control::locale}{locale},
+ {Validating Input Text}
*/
QValidator *QQuickSpinBox::validator() const
{
diff --git a/src/quicktemplates2/qquicktreeviewdelegate.cpp b/src/quicktemplates2/qquicktreeviewdelegate.cpp
index b233020702..1636afbfa1 100644
--- a/src/quicktemplates2/qquicktreeviewdelegate.cpp
+++ b/src/quicktemplates2/qquicktreeviewdelegate.cpp
@@ -216,6 +216,13 @@ void QQuickTreeViewDelegate::mousePressEvent(QMouseEvent *event)
{
QQuickAbstractButton::mousePressEvent(event);
+ if (event->buttons() != Qt::LeftButton || event->modifiers() != Qt::NoModifier) {
+ // Allow application to add its own pointer handlers that does something
+ // other than plain expand/collapse if e.g holding down modifier keys.
+ event->ignore();
+ return;
+ }
+
const auto indicator = QQuickAbstractButton::indicator();
if (indicator && indicator->isVisible()) {
const auto posInIndicator = mapToItem(indicator, event->position());
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 37b3823bde..95c1be879c 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -187,6 +187,7 @@ void QQuickWidgetPrivate::initOffscreenWindow()
{
Q_Q(QQuickWidget);
offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl);
+ offscreenWindow->setScreen(q->screen());
// Do not call create() on offscreenWindow.
QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
@@ -558,19 +559,24 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which
will resize the view to the size of the root object.
- \note QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
+ \section1 Performance Considerations
+
+ QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
The restrictions on stacking order do not apply, making QQuickWidget the more flexible
alternative, behaving more like an ordinary widget.
- \note However, the above mentioned advantages come at the expense of performance.
- Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL
+ However, the above mentioned advantages come at the expense of performance:
+ \list
+
+ \li Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL
framebuffer objects, which needs to be enforced by calling
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi) at startup.
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL) at startup.
This will naturally carry a minor performance hit.
- \note Using QQuickWidget disables the threaded render loop on all platforms. This means that
- some of the benefits of threaded rendering, for example \l Animator classes and vsync driven
- animations, will not be available.
+ \li Using QQuickWidget disables the \l{threaded_render_loop}{threaded render loop} on all
+ platforms. This means that some of the benefits of threaded rendering, for example
+ \l Animator classes and vsync driven animations, will not be available.
+ \endlist
\note Avoid calling winId() on a QQuickWidget. This function triggers the creation of
a native window, resulting in reduced performance and possibly rendering glitches. The
@@ -1035,9 +1041,7 @@ void QQuickWidgetPrivate::createContext()
context = new QOpenGLContext;
context->setFormat(offscreenWindow->requestedFormat());
- const QWindow *win = q->window()->windowHandle();
- if (win && win->screen())
- context->setScreen(win->screen());
+ context->setScreen(q->screen());
QOpenGLContext *shareContext = qt_gl_global_share_context();
if (!shareContext)
shareContext = QWidgetPrivate::get(q->window())->shareContext();
@@ -1657,19 +1661,16 @@ bool QQuickWidget::event(QEvent *e)
d->handleWindowChange();
break;
- case QEvent::ScreenChangeInternal:
- if (QWindow *window = this->window()->windowHandle()) {
- QScreen *newScreen = window->screen();
-
- if (d->offscreenWindow)
- d->offscreenWindow->setScreen(newScreen);
- if (d->offscreenSurface)
- d->offscreenSurface->setScreen(newScreen);
+ case QEvent::ScreenChangeInternal: {
+ QScreen *newScreen = screen();
+ if (d->offscreenWindow)
+ d->offscreenWindow->setScreen(newScreen);
+ if (d->offscreenSurface)
+ d->offscreenSurface->setScreen(newScreen);
#if QT_CONFIG(opengl)
- if (d->context)
- d->context->setScreen(newScreen);
+ if (d->context)
+ d->context->setScreen(newScreen);
#endif
- }
if (d->useSoftwareRenderer
#if QT_CONFIG(opengl)
@@ -1682,7 +1683,7 @@ bool QQuickWidget::event(QEvent *e)
d->render(true);
}
break;
-
+ }
case QEvent::Show:
case QEvent::Move:
d->updatePosition();